Page 1 sur 1

Arrondi sur flottant

Publié : mar. 06/nov./2007 11:55
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

Re: Arrondi sur flottant

Publié : mar. 06/nov./2007 12:45
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) 

Publié : mar. 06/nov./2007 12:48
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 :)

Publié : mar. 06/nov./2007 16:22
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 ?

Publié : mar. 06/nov./2007 18:12
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 ! :?

Publié : mar. 06/nov./2007 18:15
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

Publié : mar. 06/nov./2007 18:25
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 !!! :?

Publié : mar. 06/nov./2007 18:33
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:

Publié : mar. 06/nov./2007 18:45
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

Publié : mar. 06/nov./2007 22:15
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: