Arrondi sur flottant

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
Avatar de l’utilisateur
Ulix
Messages : 315
Inscription : ven. 04/juin/2004 14:27
Localisation : Frontignan

Arrondi sur flottant

Message par Ulix »

Bonjour a tous !

Voilà ma question :

Comment faire pour arrondir un nombre.f avec seulement deux chiffres significatif après la virgule, le reste étant a 00000000 ?

Je m'explique, je lis un fichier en mode texte, des sommes y sont sous la forme suivantes : 36.48

Je convertis cette chaine de caractaire avec l'instruction : ValF( ), j'obtiens donc un flottant = 36.47999954223633

Je que je désire c'est obtenir ça : 36.48000000000000

Comment faire cela ?

La seule instruction pouvant arrondir, Round(Nombre.f, Mode), ne permet pas de d'arrondir par exemple a deux chiffre après la virgule !

Cela m'inspire la réflexion suivante; qu'un type Monétaire sur les variables ne serait pas superflu !

Bref, quelqu'un a-t-il une solution ?

Merci d'avance !
:P
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Re: Arrondi sur flottant

Message par Backup »

Ulix a écrit :Bonjour a tous !

Voilà ma question :

Comment faire pour arrondir un nombre.f avec seulement deux chiffres significatif après la virgule, le reste étant a 00000000 ?
tu te rapelle de la fonction Print using ou l'on specifiait le masque a afficher avec des #### ??

ben Good07 nous avait fait ça, et ça marche plutot tres bien :D

Code : Tout sélectionner

;Simulation de Print Using
;Auteur Good07
;Pure Basic version 3.94
;Octobre 2005

Procedure Using(PosX,PosY,masque.s,nombre.s)
  PosDeciMasque=FindString(masque,".",1); position du point décimal du masque
  PosDeciNombre=FindString(nombre,".",1); position du point décimal du nombre (si pas de décimale renvoi zéro)
  MasqueDroite=Len(masque)-PosDeciMasque ; récupère le nombre de chiffre à droite du point décimal du masque
  MasqueGauche=Len(masque)-(MasqueDroite+1); idem à gauche
  PartieEntiere.s=Mid(nombre,1,PosDeciNombre-1); récupère la partie entière du nombre
  PartieDecimale.s=Right(nombre,Len(nombre)-PosDeciNombre); idem pour la partie décimale
  If PosDeciNombre=0
    drnombre.s=LSet(".",MasqueDroite+1,"0"); si le nombre n'est pas décimal, rajoute un point + le nombre de zéro correspondants au masque
  Else 
    drnombre.s="."+Left(PartieDecimale,MasqueDroite); si décimal coupe la partie décimale à afficher en fonction du masque
  EndIf
  Format$=RSet(Right(PartieEntiere,MasqueGauche),MasqueGauche," ")+drnombre; met le nombre au format
  StartDrawing(WindowOutput())
  DrawingMode(1)
  LongMasque=TextLength(masque); On récupère la longueur du masque en pixels
  PosChaine=LongMasque-TextLength(Format$); modifie la position d'affichage qui est différente suivant la police utilisée.
  Locate(PosX+PosChaine,PosY)
  DrawText(Format$)
  StopDrawing()
EndProcedure

If OpenWindow(0, 0, 0, 600, 400, #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget, "Test Print Using") = 0 Or CreateGadgetList(WindowID()) = 0
  End
EndIf
;-------------------------------------Exemple d'affichage
StartDrawing(WindowOutput())
DrawingMode(1)
Restore nombre
Locate(10,10)
DrawText("Affichage sans Print Using")
For i=1 To 10
  Read n.f
  res.s=StrF(n)
  Locate(10,30+pasy)
  DrawText(res)
  pasy+20
Next i
Locate(300,10)
DrawText("Affichage avec Print Using")
StopDrawing()
Restore nombre
For yy=30 To 210 Step 20
  Read n.f
  res.s=StrF(n)
  Using(300,yy,"######.##",res); appel de la procedure: on donne le masque de formatage et le nombre à formater.
Next yy


Repeat
  Event = WaitWindowEvent() 
Until Event = #PB_EventCloseWindow

End

DataSection
nombre:
Data.f 12.56364,1456.23,243676.12,789,18.475,145.89,3.14159265,45,4561,478
; IDE Options = PureBasic v3.94 (Windows - x86) 
Avatar de l’utilisateur
cederavic
Messages : 1338
Inscription : lun. 09/févr./2004 23:38
Localisation : Bordeaux

Message par cederavic »

euh....
Resultat$ = StrF(Valeur.f [, NombreDeDecimales])
Resultat$ = LSet(Chaine$, Longueur [, Caractere$])
et...

Code : Tout sélectionner

a.f = 36.48
b.s = LSet(StrF(a, 2), 17, "0")
Debug b
euh...
:P

EDIT : Bouerf j'ai lu trop vite et pris le problème à l'envers, désolé. La soluce de dobro est parfaite :)
Avatar de l’utilisateur
Ulix
Messages : 315
Inscription : ven. 04/juin/2004 14:27
Localisation : Frontignan

Message par Ulix »

Merci pour vos réponses, mais je crois que je me suis mal expliqué !

Mon problème n'est pas l'affichage, mais plutôt la représentation mémoire de la variable !
La conversion chaine-flottant entraine une imprécision le 36.48 devient : 36.47999954223633

Cette imprécision, dans des calculs pourtant simple (ex : multiplication) s'accroit, certes c'est faible, mais préfèrerais avoir après la conversion 36.48000000000000.

Y a t-il un moyen comblé (ou de remplacé) ces chiffres non significatif (surtout pour du monétaire) par des zéro ?
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message par Backup »

c'est vrais que VALF() manque un parametre pour ne tenir compte que de x chiffres apres la virgule, là nous avons tout ou rien ! :?
Dr. Dri
Messages : 2527
Inscription : ven. 23/janv./2004 18:10

Message par Dr. Dri »

ca marche pas ca ? tu manipules tes nombres normalement et tu les affiche/enregistre sous cette forme... tu n'auras jamais de valeur exacte sauf cas particuliers (0.5, 0.25 etc)

Code : Tout sélectionner

Debug StrF(35.287, 2)
Dri
Backup
Messages : 14526
Inscription : lun. 26/avr./2004 0:40

Message par Backup »

Dr. Dri a écrit :ca marche pas ca ? tu manipules tes nombres normalement et tu les affiche/enregistre sous cette forme... tu n'auras jamais de valeur exacte sauf cas particuliers (0.5, 0.25 etc)

Code : Tout sélectionner

Debug StrF(35.287, 2)
Dri
oui mais la valeur Flottant dans une variable flottante reprendra l'imprecision !!

si tu fait

Code : Tout sélectionner


b.s=StrF(36.48 , 2)
Debug b.s

; ok il est arrondi

a.f=ValF(b.s)
Debug a.f

; la valeur interne de la variable est toujours imprecise !!
; et perd l'arrondi !!!

seul les variable chaines garde les arrondis !!! :?
Avatar de l’utilisateur
Ulix
Messages : 315
Inscription : ven. 04/juin/2004 14:27
Localisation : Frontignan

Message par Ulix »

Désoler Dr.Dri !
Mon problème n'est pas l'affichage, mais plutôt la représentation mémoire de la variable !
La conversion chaine-flottant entraine une imprécision le 36.48 devient : 36.47999954223633
Je désirerais que le nombre.f soit égale a 36.48000000000000

Dobro a écrit
; la valeur interne de la variable est toujours imprecise !!
; et perd l'arrondi !!!
Tout a fait exacte !

Merci quand même :wink:
Avatar de l’utilisateur
case
Messages : 1546
Inscription : lun. 10/sept./2007 11:13

Message par case »

une solution serait de conserver des strings en mémoire
et de ne pas les convertir en float pour les calculs

mais d'effectuer les calculs 'a la main ' de la même façon qu'a l'école ^^
voila un début de solution addition et multiplications ne geres pas les nombres negatifs

Code : Tout sélectionner

Declare.s multiply(st1$,st2$)
Declare.s addition(st1$,st2$)
Declare.s soustraction(st1$,st2$)


Debug multiply("20.5","35.0")
Debug addition("20.2","35.58")

Procedure.s multiply(st1$,st2$)
	V1=Len(st1$)-FindString(st1$,".",1) ; nombre de decimales
	V2=Len(st2$)-FindString(st2$,".",1) ; nombre de decimale
	If v1=Len(st1$):v1=0:EndIf
	If v2=Len(st2$):v2=0:EndIf
	st1$=RemoveString(st1$,".")
	st2$=RemoveString(st2$,".")
	;multiplication
	result$=Str(Val(st1$)*Val(st2$))
	result$=(Left(result$,Len(result$)-(V1+V2))+"."+Right(result$,(v1+v2)))	
	ProcedureReturn result$
EndProcedure
Procedure.s addition(st1$,st2$)
	V1=Len(st1$)-FindString(st1$,".",1) ; nombre de decimales
	V2=Len(st2$)-FindString(st2$,".",1) ; nombre de decimale
	;
	If v1=Len(st1$):v1=0:EndIf
	If v2=Len(st2$):v2=0:EndIf
	If v2<>V1
		If v1>V2
			If v2=0 ; pas de virgule
				st2$=st2$+"."+Space(V1-v2)
			Else
				st2$=st2$+"."+Space(V1-v2)
			EndIf
		EndIf
		If v2>v1
			If v1=0 ; pas de virgule
				st1$=st1$+Space(V2-v1)
			Else
				st1$=st1$+Space(V2-v1)
			EndIf
		EndIf
	EndIf
	If Len(st1$)>Len(st2$)
		st2$=Space(Len(st1$)-Len(st2$))+st2$
	EndIf
	If Len(st2$)>Len(st1$)
		st1$=Space(Len(st2$)-Len(st1$))+st1$
	EndIf
	ret=0
	For a=Len(st1$) To 1 Step -1
		If Mid(st1$,a,1)<>"."
			ad=Val(Mid(st1$,a,1))+Val(Mid(st2$,a,1))
			If ad>10
				ad=ad-10
				ret=1
			Else
				ret=0	
			EndIf
			result$=Str(ad)+result$
		Else
			result$="."+result$
		EndIf
	Next
	
If v2=0 And v1=0 ; pas de virgule on enleve les zero au debut
	result$=ReplaceString(LTrim(ReplaceString(result$,"0"," "))," ","0")
Else ; on enleve les zero des deux cotes
	result$=ReplaceString(LTrim(RTrim(ReplaceString(result$,"0"," ")))," ","0")
EndIf
	ProcedureReturn result$
EndProcedure

bon courage pour tout implémenter personnellement je ne pense pas aller plus loin pour l'instant.

edit :correction de bug le code enlevait les zero en debut et fin de resultat meme si il n'y avais pas de virgule

050 devenais 5 au lieu de 50
ImageImage
lionel_om
Messages : 1500
Inscription : jeu. 25/mars/2004 11:23
Localisation : Sophia Antipolis (Nice)
Contact :

Message par lionel_om »

Si tu veux garder uniquement deux décimales, rien ne t'empeche de mémoriser ta valeur en la multipliant par 100 ou 1000 dans un entier (ou un float) et quand tu veux l'afficher, tu n'as plus qu'à diviser par la bonne valeur et afficher la valeur entière...

Lio :wink:
Webmestre de Basic-univers
Participez à son extension: ajouter vos programmes et partagez vos codes !
Répondre