Comment forcer Delphi à prendre en compte l'epsilon quand il fait un = avec des variables de type extended et/ou float ? Je connais la fonction CompareValue qui fait ça mais moi j'aimerais que Delphi le fasse nativement sur le =.
Comment forcer Delphi à prendre en compte l'epsilon quand il fait un = avec des variables de type extended et/ou float ? Je connais la fonction CompareValue qui fait ça mais moi j'aimerais que Delphi le fasse nativement sur le =.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 type TSingle = record Value: Single; class operator Implicit(a: Single): TSingle; class operator Equal(a, b: TSingle): Boolean; class operator NotEqual(a, b: TSingle): Boolean; end; class operator TSingle.Implicit(a: Single): TSingle; begin Result.Value := a; end; class operator TSingle.Equal(a, b: TSingle): Boolean; begin Result := Abs(a.Value - b.Value) < Epsilon; end; class operator TSingle.NotEqual(a, b: TSingle): Boolean; begin Result := Abs(a.Value - b.Value) >= Epsilon; end; begin var s1, s2, s3: TSingle; s1 := 1; s2 := 1 + Epsilon / 2; s3 := 2; assert(s1 = s2); assert(s1 <> s3); end.
Je trouvais l'idée intéressante de pouvoir créer un nouveau type exemple TNumber qui serait en interne un extended.
Avec des implicit qui convertit en integer ou en string etc....
Par contre ça devient vite fastidieux à faire, il faut refaire tous tous tous les opérateurs possibles avec tous les types possibles qui peuvent interagir lolllll.
Moi tout ce dont j'ai besoin c'est de régler le problème de ce bout de code :
Au bout de 3 tours tempval vaut 1 mais le < 1 répond false.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 var tempVal: extended; begin tempVal := 0.001; while (tempVal < 1) do begin tempVal := tempVal * 10; end; end;
En prenant en compte 'epsilon dans la comparaison, je peux régler ça avec CompareValue :
Mais je ne veux pas commencer à mettre des CompareValue partout dans mon programme, ça devient compliqué.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 var tempVal: extended; begin tempVal := 0.001; while (CompareValue(tempVal, 1) = -1) do begin tempVal := tempVal * 10; end; end;
Je chercher à overrider les opérateurs = < > pour qu'il utilise le CompareValue nativement.
dans ce cas utilise des entiers
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 var tempVal: Integer; begin tempVal := 1; while (tempVal < 1000) do begin tempVal := tempVal * 10; end;
Je ne peux pas, hehehe les valeurs qui vont rentrer dans ma fonction sont des valeurs monétaires.
Pour l'exemple j'ai mis tempVal := 0.001 mais en vrai, tempval va venir d'une valeur monétaire passée en paramètre. Je dois compter quelle est la profondeur des décimales du résultat selon la position du digit le plus haut.
Exemple :
4.0 va donner 0
56.76 va donner 1
124.45 va donner 2
0.4 donne -1
0.0232 donne -2
0.001 va donner -3
0.00000006898 donne -8
Pour l'exemple j'ai mis seulement le while qui vérifie en bas de 1.
currency alors, ça a l'apparence d'un extended mais c'est un entier à virgule fixe en fait.
Currency est un type de données à virgule fixe. L'erreur maximale lors de la conversion de virgule flottante en Currency est de 0.00005.
Si c'est du monétaire, il faut respecter quelques règles comptables avec le compte d'arrondi pour gérer l'égalité d'une somme lors d'une conversion de devises.
En particulier depuis l'Euro qui impose un calcul sur 6 digits pour gérer correctement l'arrondi vers le Currency (puis vers le Centime)
Ou alors il manque une généricité à votre code, peut-être travailler sur des objets métiers qui regroupent ce besoin précis.
Si c'est juste compter le nombre de digits après la virgule, il y a surement plus simple, pourquoi ne pas un ToString puis compter la position du premier chars <> 0 avant et après le séparateur décimal ... sans parler des fonctions de Logarithme
je pense qu'il y a une évidence mathématique :
Floor(Log10(4,0)) = Floor(0,6020599...) = 0
Floor(Log10(56,76)) = Floor(1,7540423...) = 1
Floor(Log10(124,45)) = Floor(2,0949949...) = 2
Floor(Log10(0,4)) = Floor( -0,3979400...) = -1
Floor(Log10(0,0232)) = Floor(-1,6345120151...) = -2
Floor(Log10(0,001)) = Floor(-3)
Floor(Log10(0,00000006898)) = Floor(-7,161276...) = -8
Floor je pense, arrondi positif vers le bas et le négatif vers le prochain entier inférieur (donc -7.5 devient -8 si je me trompe pas)
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
Quand je teste en Delphi :
Log10(0,001) = -3
Floor(Log10(0,001)) = -4
Sûrement encore mon problème d'epsilon lolllllllll
J'imagine que Log10(0.001) doit donner quelque chose comme -3.0000000000001
Pourquoi ne pas regarder ici : https://blog.grijjy.com/2021/05/05/high-precision/
J'ai parfois aussi utilisé la fonction RoundTo qui permet de ne conserver que les X décimales significatives voulues (ça évite de se retrouver avec un 3.00000000001 après calcul par exemple)
Cela me fait penser à une solution un peu pourri que j'avais utilisé d'arrondir en deux temps, l'un pour purger l'epsilon en quelque sorte pour que le second ne derive pas trop comme le Floor(-3) qui donne -4 ce qui est aberrant !
Que donne, en théorie, j'ai pas Delphi sous la main, uniquement pour les valeurs < 0
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 //--------------------------------------------------------------------------- procedure SimpleRoundToE(const AValue: Extended; const ADigit: TRoundToRange = -2): Extended; begin if ADigit < 0 then Result = SimpleRoundTo(SimpleRoundTo(AValue, ADigit - 2), ADigit) else Result = SimpleRoundTo(AValue, ADigit); end;
Floor(SimpleRoundTo(Log10(0,001), -2) = -4
Floor(SimpleRoundTo(-3.0000000000001, -2)) = -3 ?
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
les entiers c'est tout de même plus simple...dans un de mes derniers projets je suis d'ailleurs passé par là pour un problème d'arrondi, la version utilisant FMod partant en vrille.
Je code une solution crypto...
Les valeurs peuvent aller de + 18 a -18 digits
999999999999999999,999999999999999999
(si je ne me trompe pas)
Tout ça sans aucune erreur de calcul ou de comparaison à la 18ième decimale.
Même si je prends un extended qui a encore plus de précision, ça reste que 0 n'est pas egal a 0.000000000000000000000000000000000000000000000000000000000000000001
hehehehe
Je suis en train de faire des fonctions comme les if en DOS :
EQU : Equal
NEQ : Not equal
LSS : Less than <
LEQ : Less than or Equal <=
GTR : Greater than >
GEQ : Greater than or equal >=
Ouein ça c'est une autre chose quand je vais avoir fixé mes fonctions, je vais devoir tester avec des nombres négatifs.
Mais avant je dois au minimum être capable de faire mes 6 fonctions sans aucune erreur à la 18ième décimale.
pour la cryptologie, quand je n'utilise pas les fonctions de l'OS ou d'OpenSSL, j'utilise BigInteger
j'ai lus quelque par que biginteger est pas complet, ya encore des bug pis qu'il a des probleme avec le mod.
Bon je commence un type Number qui vas prendre 3 integer pour les entier et 3 pour les fraction, je vais integer tout les operator que je vais avoir besoin au fur et a mesure....
ma pouvoir prendre les nombre :
9903520300447984150353281023.9903520300447984150353281023
faque ma surrement limiter mes valeur a :
999999999999999999999999999.999999999999999999999999999
avez vous des conseils ? je pense je m'embarque dans quelque chose de hazardeux comme terrain...
Comment je peux controller comment un Number vas etre affecter a un autre Number?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 type Number= record Value: integer; class operator Implicit(a: Number): Number; <---- E2521 Operator 'Implicit' must take one 'Number' type in parameter or result type end;
tu as lu ça où ?
en quoi est-il incomplet ?
PS: à ma connaissance Rudy Velthuis est décédé, ce repository ne bougera sans doute plus.
Je l'utilise dans mon compilateur APKPascal pour signer les APK et n'ai rencontré aucun soucis.
bon au moin ca l'air a marcher pour avoir des tres gros chiffre...
au fait comment on fait pour inclure un unit qui est dans un sous repertoire ? BigNumbers a trop de fichier, j'aimerais le laisser dans un sous folder BigNumbers pour garder les dependence clair.
tout cas merci Paul, ma dire que ca me tentais pas de me tapper la job que Rudy a fait pour faire son BigNumber.... offf mais il faut ce qu'il faut, j'aurrais fait une version simplifier de ca, mais neanmoin ca aurrais pris beaucoup de temp.
merci aussi pour l'aide que tu m'a apporter avec le websocket. j'ai reussi a faire une version tres raisonable que j'utilise pour faire mon programme.
Le processeur effectue les calculs en base 2. On sait donc exprimer un demi, un quart, un huitième, etc. soit 1 / 2^n mais on ne sait pas décrire 1/10 ou 1/100 qui sont des nombres incommensurables en base 2. Voilà donc un résultat de cette anomalie.
Tu peux reproduire le problème avec n'importe quel outil. Voir exemple ci joint en excel
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager