[php] Floats vergelijken, casten als string?

Pagina: 1
Acties:

Onderwerpen

Vraag


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
We hebben een probleem met het vergelijken van floats in PHP. Even een dummy-voorbeeld:

PHP:
1
2
3
4
$float_a = 58/4;
$float_b = 116/8;

$float_a === $float_b; // zou TRUE moeten zijn, is FALSE


Dit is een bekend probleem: het vergelijken van floats is onbetrouwbaar http://php.net/manual/en/language.types.float.php. De aldaar genoemde 'epsilon'-methode kunnen we niet gebruiken:

PHP:
1
($float_a-$float_b) < 0.001; // TRUE


omdat betreffende code de precisie niet kan weten. Wat ons echter opviel is dat de 'string-versie' van de floats gelijk zijn. In het dummy-voorbeeld: "14.5".

PHP:
1
(string) $float_a === (string) $float_b; // TRUE


Some 'dude from the internet' raadt dit ook aan op StackOverflow (https://stackoverflow.com...ts-in-php#answer-26799533).

Is dit nu een goede oplossing (het voelt hackish), of gaat dit false negatives/positives opleveren? Er even van uitgaand dat we de precisie van de float dus niet (kunnen) weten.

Beste antwoord (via Rekcor op 19-04-2018 16:03)


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Rekcor schreef op donderdag 19 april 2018 @ 15:50:
Wat ik zat te denken op basis van je posts: kan ik die significantie niet automatisch bepalen?
Ja en nee. Dat is nogal afhankelijk van je eisen / klant / opdracht / whatever natuurlijk. En waar jij wel/geen genoegen mee neemt.

Pak pi; daar is 't afhankelijk van de "leraar" / "examinator" / whatever om te bepalen hoeveel decimalen hij/zij "good enough" vindt. Dus daar zul je toch de epsilon voor in de GUI beschikbaar moeten maken.

Wil je het "automatisch" doen: Je zou op basis van het (bekende) antwoord kunnen zeggen: er zijn 6 decimalen, dan gaan we (bijv.) op 6 decimalen vergelijken en is de epsilon dus 0.000001. Maar indirect voert de "leraar" / "examinator" / whatever dan toch nog een "epsilon" in; hij/zij voert immers 3.141592 in als "correct antwoord". Als hij/zij als antwoord 3.14 invoert zou je epsilon dan vanzelf 0.01 zijn. Of 0.001; net hoe je 't implementeert natuurlijk.

Het hele punt is wat je aan "foutmarge" accepteert. En wil je 0 foutmarge dan heb je gewoon überhaupt het verkeerde type (float) gekozen en zul je voor (bijv.) fixed integer of bcmath of andere exacte(re) types moeten gaan.

[ Voor 16% gewijzigd door RobIII op 19-04-2018 15:59 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij

Alle reacties


Acties:
  • 0 Henk 'm!

  • Morrar
  • Registratie: Juni 2002
  • Laatst online: 14:45
Je zou ook met X (zeg 1000) kunnen vermenigvuldigen, rounden naar int en dan kunnen vergelijken. Heb je wellicht nog iets meer idee van / controle over de maximale precisie.

Acties:
  • 0 Henk 'm!

  • TeraMod
  • Registratie: Juli 2017
  • Laatst online: 04-05-2024

TeraMod

Cloud Enablement Desk

Ik zie enkele malen voorbij komen dat een round() het probleem ook zou "verhelpen".

code:
1
round($float_a, 3) === round($float_b, 3)


Eerlijk gezegd voelt dit ook nog als een hack, maar gevoelsmatig toch minder hackig als een string conversion ;)

Acties:
  • +4 Henk 'm!

  • ValHallASW
  • Registratie: Februari 2003
  • Niet online
Zonder meer informatie over het probleemdomein is deze vraag niet zinnig te beantwoorden. Wat representeren de waardes die je wilt vergelijken, en waarom is het niet mogelijk om een zinnige epsilon te definieren? (en, als vervolgvraag daarop: waarom is de ~10^-17 relatieve precisie van een double dan voldoende?).

Als het gaat om het vergelijken van breuken zoals in je voorbeeld: geen floats gebruiken maar je breuken als breuken opslaan, bv met https://github.com/phospr/fraction.

(Overigens is je voorbeeld wat vreemd gekozen, aangezien 58/4 en 116/8 als floats exact hetzelfde zijn)

[ Voor 9% gewijzigd door ValHallASW op 18-04-2018 19:11 ]


Acties:
  • 0 Henk 'm!

  • GlowMouse
  • Registratie: November 2002
  • Niet online
omdat betreffende code de precisie niet kan weten
De precisie van floats is goed gedocumenteerd. Bedoel je de orde van grootte? Dan vergelijk je toch met 10-13*a ipv 0.001?

[ Voor 6% gewijzigd door GlowMouse op 18-04-2018 19:26 ]


Acties:
  • 0 Henk 'm!

  • Feanathiel
  • Registratie: Juni 2007
  • Niet online

Feanathiel

Cup<Coffee>

Om even een voorbeeld te geven van wat @ValHallASW bedoeld:

PHP:
1
2
3
4
$a = 1e32; // of eigenlijk 100000003318135351409612647563264
$b = 1e33 / 10; // of eigenlijk 999999994495727286427992885035008 / 10

$c = abs($a - $b); // 1.8014398509482E+16


Als je met verschillende getallen werkt die erg groot/klein of een combinatie hiervan zijn, dan kom je gewoon niet uit met die 0.001 (factor ~1e+19 verschil).

Toolje om getallen wat inzichtelijker te maken, ook al ga je 'm niet vaak nodig hebben: https://www.h-schmidt.net/FloatConverter/IEEE754.html

Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Dank!

De meeste antwoorden heb ik al voorbij zien komen in mijn zoektocht. Voor al die antwoorden moet je echter de precisie weten, en die kan ik niet weten. Ik vergelijk namelijk user inputs met berekende waarden op basis van die user inputs. Het is voor de use case noodzakelijk dat de user zelf de precisie kan bepalen.

Mijn vraag is dus eigenlijk: hoe floats the vergelijken zonder dat je de precisie weet en indien dit niet mogelijk is: wat kan er mis gaan met die string comparison?

Acties:
  • 0 Henk 'm!

  • Onbekend
  • Registratie: Juni 2005
  • Laatst online: 19:59

Onbekend

...

Heb je echt eindgetallen die vergeleken moeten worden of getallen die onder en boven de deelstreep komen?

Stel je hebt dit:
PHP:
1
2
$float_a = 9/8;
$float_b = 900000001/800000000;

Moet dit volgens jou gelijk zijn aan elkaar of niet? Zo nee, dan moet je op basis van je gebruikte getallen de nauwkeurigheid bepalen.

Maar stel dat je die 4 losse getallen hebt:
PHP:
1
2
3
4
5
6
$a1 = 9;
$a2 = 8;
$b1 = 900000001;
$b2 = 800000000;
$float_a =  $a1 / $a2;
$float_b = $b1 / $b2;


Dan kan je gemakkelijk de getallen met elkaar vermenigvuldigen, en heb geen last van cijfers achter de komma:
PHP:
1
2
3
4
5
6
$a1 = 9;
$a2 = 8;
$b1 = 900000001;
$b2 = 800000000;
$float_c =  $b2 * $a1;
$float_d =  $a2 * $b1;

Speel ook Balls Connect en Repeat


Acties:
  • 0 Henk 'm!

  • Morrar
  • Registratie: Juni 2002
  • Laatst online: 14:45
Een float heeft altijd een limiet aan de precisie, deze wordt namelijk gedicteerd door de ruimte in het geheugen waarin de float wordt opgeslagen. Als je naar string converteert zit je dus evengoed vast aan een bepaalde precisie, namelijk de precisie zoals bepaalt door de programmeertaal waar je in werkt.

Misgaan kan er niet zoveel als je naar string converteert, alleen is strings vergelijken in vrijwel alle implementaties langzamer. Hoeveel langzamer is afhankelijk van hoe de strings in het geheugen worden opgeslagen. Voor UTF8 heb je bijvoorbeeld al 8 bits per karakter nodig; voor het getal 120 dus 3 x 8 = 24 bits (vgl als integer neemt dit getal 7 bits in: 1111000). In diezelfde geheugenruimte kun je een veel grotere numerieke waarde kwijt. Hierdoor is vergelijken van numerieke waardes over het algemeen sneller.

[ Voor 25% gewijzigd door Morrar op 18-04-2018 21:06 ]


Acties:
  • 0 Henk 'm!

  • Josk79
  • Registratie: September 2013
  • Laatst online: 05:25
Wat is er mis met bijv.

code:
1
if ( abs($a-$b) < 1e-10 ) echo "gelijk";

Of verwacht je dat men grotere precisie nodig heeft?

Acties:
  • 0 Henk 'm!

  • biomass
  • Registratie: Augustus 2004
  • Laatst online: 18:38
Rekcor schreef op woensdag 18 april 2018 @ 20:49:
Dank!

De meeste antwoorden heb ik al voorbij zien komen in mijn zoektocht. Voor al die antwoorden moet je echter de precisie weten, en die kan ik niet weten. Ik vergelijk namelijk user inputs met berekende waarden op basis van die user inputs. Het is voor de use case noodzakelijk dat de user zelf de precisie kan bepalen.

Mijn vraag is dus eigenlijk: hoe floats the vergelijken zonder dat je de precisie weet en indien dit niet mogelijk is: wat kan er mis gaan met die string comparison?
String comparison of tolerance value: Zodra je veel significante cijfers nodig hebt, moet je gaan kiezen - hoeveel tekens moeten er vergeleken worden. Ofwel hetzelfde probleem in een andere verpakking.
Je geeft zelf je antwoord: Schrijf vijf functies die de operators =, >=, <=,>, < implementeren met de notie van precisie. De laatste twee zijn complementair aan nummer twee en drie, wat inhoud dat 0.999 op die manier *niet* kleiner is dan 1 wanneer de tolerance waarde 0.1 is. Ik bedoel maar :P

Hoeveel significante cijfers verwacht je nodig te hebben?

Acties:
  • 0 Henk 'm!

  • DroogKloot
  • Registratie: Februari 2001
  • Niet online

DroogKloot

depenisvanjezus

Wat is er mis met bijv.

code:
1
if ( abs($a-$b) < 1e-10 ) echo "gelijk";
Alles, want de precisie van floats *varieert* zoals eerder aangegeven: een epsilon van 1e-10 werkt prima wanneer $a en $b in de buurt van 0 liggen, maar is compleet hopeloos voor waardes rond (bv) +1e20.
hoe floats the vergelijken zonder dat je de precisie weet
Als een absolute precisie van 0.001 of welke je ook kiest nodig is bij alle mogelijke outputs (wat mij sterk lijkt, maar goed), dan is het enige antwoord hier om floats overboord zetten en een fixed-point type te gebruiken waarin altijd "genoeg" bits voor het deel achter de komma beschikbaar zijn.

Acties:
  • +3 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Rekcor schreef op woensdag 18 april 2018 @ 17:52:
Dit is een bekend probleem: het vergelijken van floats is onbetrouwbaar
Bekend probleem? Onbetrouwbaar? Het is gewoon gedocumenteerd en gedefinieerd. Dat 't niet doet wat jij verwacht / zou willen is een ander verhaal. Zie ook onze Getallen en talstelsels FAQ (ook voor alternatieven).
Rekcor schreef op woensdag 18 april 2018 @ 17:52:
De aldaar genoemde 'epsilon'-methode kunnen we niet gebruiken:

PHP:
1
($float_a-$float_b) < 0.001; // TRUE


omdat betreffende code de precisie niet kan weten.
Wat natuurlijk onzin is. Als ik een plankje zaag voor in een kast kunnen 2mm een behoorlijk verschil maken, als ik de afstand naar de maan meet zijn 2mm verschil nogal verwaarloosbaar. Je wil de epsilon dus afhankelijk maken van de orde-grootte waarin je werkt. En dat is prima af te leiden van de getallen waarmee je werkt.
Rekcor schreef op woensdag 18 april 2018 @ 17:52:
Some 'dude from the internet' raadt dit ook aan op StackOverflow
Er zijn zoveel "dudes" die vanalles en nog wat roepen en uitkramen. Het is echter niet voor niets maar 4 keer ge-upvote i.t.t. het geaccepteerde antwoord met 172 upvotes. Maar die past niet (precies genoeg) in je straatje en dan ga je je maar in ontzettend rare bochten wringen (zoals floats naar strings casten e.d.) i.p.v. een stapje terug te doen en je probleem nog eens te bekijken en te (her)evalueren hoe je 't wil gaan aanpakken. Althans; zo komt het op mij (behoorlijk) over.

Overigens ben ik 't (ook) niet helemaal eens met de vaste epsilon zoals in 't geaccepteerde antwoord op SO; maar dat wordt daar in de comments ook al aangekaart; daarbij is deze methode van vergelijken wat robuuster in edge-cases. Maar de epsilon kun je prima laten afhangen van je inputs (bijv. X% van de kleinste/grootste input. Hier nog meer wijsheden, naast de links in onze FAQ natuurlijk ;)

Het zou al behoorlijk helpen als je aangaf wat die getallen representeren. In doorsnee real-world usage heb je in de meeste gevallen al genoeg aan 2 tot 4 significante cijfers dus tenzij je een van deze erg specifieke gevallen (NASA engineer toevallig? :P ) bent denk ik dat je toch eens moet overwegen hoe precies je het wil hebben (en of floats dan überhaupt wel de oplossing zijn en je misschien beter kunt gaan kijken naar "exactere" alternatieven; misschien wel buiten PHP in een andere taal?)

[ Voor 24% gewijzigd door RobIII op 19-04-2018 00:09 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Josk79
  • Registratie: September 2013
  • Laatst online: 05:25
DroogKloot schreef op woensdag 18 april 2018 @ 22:21:
Alles, want de precisie van floats *varieert* zoals eerder aangegeven.
Nou, 'alles' is een tikkie overdreven. Het hangt een beetje van de usecase af. Wellicht kan TS een arbitraire epsilon gebruiken.

String gebruiken doet nagenoeg hetzelfde vieze truukje, afhankelijk van de ini setting 'precision'.

Ik weet trouwens niet of de float-precision in php platform onafhankelijk is?

Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
RobIII schreef op woensdag 18 april 2018 @ 23:52:
[...]

Het zou al behoorlijk helpen als je aangaf wat die getallen representeren. In doorsnee real-world usage heb je in de meeste gevallen al genoeg aan 2 tot 4 significante cijfers dus tenzij je een van deze erg specifieke gevallen (NASA engineer toevallig? :P )
Het betreft antwoorden van studenten in een onderwijssetting. Ze moeten hun meetgegevens invoeren, met die meetgegevens een (chemische) constante uitrekenen en het script controleert dan of die berekening goed is gegaan.

Het ligt er dus maar net aan hoe ze gemeten hebben (de meting kan op meerdere manieren, precieze en minder precieze) en welke andere tools (Excel, rekenmachine, papier) ze gebruiken met welke precisie ze aankomen. Hen wordt tevens geleerd om pas helemaal aan het einde van de berekening af te ronden in hun verslag.

Het script wat de berekeningen controleert is tevens generiek: in dit geval controleren we meetgegevens, maar in veel andere gevallen controleert het script hele andere dingen. Het is de duur om voor deze specifieke case een heel maatwerk script te bouwen en te onderhouden.

Kortom: m.i. zijn de (externe) design requirements dusdanig, dat ik niet anders kan dan een 'minst slechte' oplossing te kiezen. Dat heeft niets te maken met koppigheid van mijn kant, maar met een niet-ideale wereld die we proberen te modelleren in een ideal (nu ja, PHP ;)) te modelleren.

Ik sta natuurlijk geheel open voor suggesties die rekening houden met de geschetste design requirements :-).

Acties:
  • +1 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
8)7 Dan weet je toch prima wat de foutmarge is die je wil tolereren? Je bepaalt gewoon het aantal significante cijfers (of decimalen), dus zeg maar de foutmarge waar de student binnen moet blijven, en geeft daar de bijbehorende epsilon van in voor die opdracht. Ik zie het probleem niet...

PHP:
1
2
3
function NearlyEqual($a, $b, $epsilon) {
  return abs($a-$b) < $epsilon
}


Hebben ze pi berekend dan gebruik je NearlyEqual($user_input, $pi, 0.00001) bijv. en hebben ze de snelheid van het licht berekend in m/s dan gebruik je NearlyEqual($user_input, $c, 1) (of zelfs 10 of 100 ofzo voor epsilon) en hebben ze de lengte van een A4 gemeten in mm dan gebruik je NearlyEqual($user_input, $length, 0.1). Ik zie écht je hele probleem niet.

Bovenstaande functie zal in 99% van de gevallen prima voldoen; voor de edge-cases kun je dan RobIII in "[php] Floats vergelijken, casten als string?" nog eens goed bekijken.

[ Voor 43% gewijzigd door RobIII op 19-04-2018 09:49 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • TheNephilim
  • Registratie: September 2005
  • Laatst online: 17-09 11:07

TheNephilim

Wtfuzzle

Rekcor schreef op woensdag 18 april 2018 @ 20:49:
[...]
Het is voor de use case noodzakelijk dat de user zelf de precisie kan bepalen.
[...]
Als de user de precisie zelf kan bepalen, dan werk je toch niet met een vaste precisie? Dan kun je dus code gebruiken die rekening houdt met de door de user geselecteerde precisie.

Acties:
  • 0 Henk 'm!

  • pimlie
  • Registratie: November 2000
  • Laatst online: 16-09 23:04
Als je met hoge wiskundige precisie moet werken in php is het vaak het beste om een van de mathematical extensies te gebruiken, bv GMP of BCMath

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
pimlie schreef op donderdag 19 april 2018 @ 09:36:
Als je met hoge wiskundige precisie moet werken in php is het vaak het beste om een van de mathematical extensies te gebruiken, bv GMP of BCMath
Dat is handig als je allerlei berekeningen doet en op elkaar stapelt enz. en tijdens dat hele proces zoveel mogelijk precisie wil behouden etc. maar dat is hier allemaal niet aan de orde. Er wordt namelijk niet gerekend. Hier gaat 't om user-input die (binnen bepaalde marge) gelijk moet zijn aan een constante om te bepalen of een vraag goed/fout beantwoord is. Dat is echt geen rocket science en prima op te lossen met de "epsilon-methode".

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • pimlie
  • Registratie: November 2000
  • Laatst online: 16-09 23:04
@RobIII daarom zei ik ook vaak en niet altijd. Hoe dan ook is het een mogelijke oplossing voor @Rekcor's probleem, of het een zinvolle oplossing is is aan hem om te bepalen en niet aan jou.

En sterker nog, als je kijkt naar bccomp dan bevestigd die functie juist dat bccomp intern zelf ook een soort van epsilon-methode gebruikt. Als de user input van een webform komt kan het echter handig zijn om bccomp te gebruiken omdat die standaard al string's als input accepteert, dan hoef je je daar niet druk om te maken of te vertrouwen op php's type juggling.

Ik snap dus écht je hele probleem niet met mijn informatieve post 8)7

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
pimlie schreef op donderdag 19 april 2018 @ 10:26:
Ik snap dus écht je hele probleem niet met mijn informatieve post 8)7
Nou nou nou... Mijn hele "probleem" is dat 't overkill is voor zoiets simpels (los van 't feit dat 't maar de vraag is of die onderdelen aanwezig zijn / mee-compiled zijn of niet). Het is aan TS om de informatie van je post en de mijne op waarde te schatten ;) :*

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • pimlie
  • Registratie: November 2000
  • Laatst online: 16-09 23:04
Die opmerking was een verwijzing naar je eigen posts he :+ O+

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
pimlie schreef op donderdag 19 april 2018 @ 10:32:
[...]


Die opmerking was een verwijzing naar je eigen posts he :+ O+
offtopic:
Weet ik, maar vond 't niet terecht ;) Ander geval. Maar boeie, laten we ontopic proberen blijven :*

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • emnich
  • Registratie: November 2012
  • Niet online

emnich

kom je hier vaker?

Rekcor schreef op woensdag 18 april 2018 @ 20:49:
Dank!

De meeste antwoorden heb ik al voorbij zien komen in mijn zoektocht. Voor al die antwoorden moet je echter de precisie weten, en die kan ik niet weten. Ik vergelijk namelijk user inputs met berekende waarden op basis van die user inputs. Het is voor de use case noodzakelijk dat de user zelf de precisie kan bepalen.

Mijn vraag is dus eigenlijk: hoe floats the vergelijken zonder dat je de precisie weet en indien dit niet mogelijk is: wat kan er mis gaan met die string comparison?
Dit spreekt elkaar tegen. Als de gebruiker het zelf mag bepalen, dan weet je het. Als je het niet weet dan moet je daar een aanname voor doen. Wat voor getal je daarbij kiest kunnen wij moeilijk beoordelen omdat we niet weten waar het over gaat en hoe belangrijk het is. Ontploft de boel bijvoorbeeld bij een false positive of wordt er slechts een vraag bij een proefexamen foutief goed gerekend.Je zal daar zelf (of de gebruiker of opdrachtgever) een beslissing over moeten nemen.

Zodra je de precisie weet kan je het prima met PHP controleren.

Acties:
  • 0 Henk 'm!

  • indigo79
  • Registratie: Februari 2009
  • Niet online
DroogKloot schreef op woensdag 18 april 2018 @ 22:21:
[...]


Alles, want de precisie van floats *varieert* zoals eerder aangegeven: een epsilon van 1e-10 werkt prima wanneer $a en $b in de buurt van 0 liggen, maar is compleet hopeloos voor waardes rond (bv) +1e20.
PHP:
1
if(abs($a - $b) / abs($a + $b) < 1.e-6 ) echo "gelijk";


lost dat op (je kan in de noemer ook abs(($a + $b) / 2.) (echt gemiddelde, geeft je een extra deling) of abs($a) of abs($b) (een van beide getallen, als ze ongeveer gelijk zijn, maakt het nauwelijks uit welk van de twee je gebruikt en als ze niet ongeveer gelijk zijn, zit je toch ver over je grens) gebruiken.

Acties:
  • 0 Henk 'm!

  • GlowMouse
  • Registratie: November 2002
  • Niet online
code:
1
Warning: Division by zero in Command line code on line 1

Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
RobIII schreef op donderdag 19 april 2018 @ 09:22:
[...]

8)7 Dan weet je toch prima wat de foutmarge is die je wil tolereren? Je bepaalt gewoon het aantal significante cijfers (of decimalen), dus zeg maar de foutmarge waar de student binnen moet blijven, en geeft daar de bijbehorende epsilon van in voor die opdracht. Ik zie het probleem niet...
Nogmaals: de foutmarge kan ik - als dev - niet weten, dat is te afhankelijk van de specifieke vraag of opdracht. Dit zou de docent dan moeten invoeren in het systeem, maar dat betekent extra GUI, die in veel gevallen ook niet nodig is.

En eigenlijk vindt ik dit de omgekeerde wereld: de eindgebruiker extra werk geven omdat PHP anno 2018 niet zeker kan weten dat 1.1345 === 1.1345...

Wat ik zat te denken op basis van je posts: kan ik die significantie niet automatisch bepalen? Met andere woorden, over alle bekende variabelen itereren, een functie als NumberBreakDown (https://stackoverflow.com...f-a-number#answer-6619392) draaien, het maximale aantal decimalen achter de komma bepalen en dat gebruiken in een functie als nearlyEqual van http://floating-point-gui.de/errors/comparison/?

Acties:
  • Beste antwoord
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Rekcor schreef op donderdag 19 april 2018 @ 15:50:
Wat ik zat te denken op basis van je posts: kan ik die significantie niet automatisch bepalen?
Ja en nee. Dat is nogal afhankelijk van je eisen / klant / opdracht / whatever natuurlijk. En waar jij wel/geen genoegen mee neemt.

Pak pi; daar is 't afhankelijk van de "leraar" / "examinator" / whatever om te bepalen hoeveel decimalen hij/zij "good enough" vindt. Dus daar zul je toch de epsilon voor in de GUI beschikbaar moeten maken.

Wil je het "automatisch" doen: Je zou op basis van het (bekende) antwoord kunnen zeggen: er zijn 6 decimalen, dan gaan we (bijv.) op 6 decimalen vergelijken en is de epsilon dus 0.000001. Maar indirect voert de "leraar" / "examinator" / whatever dan toch nog een "epsilon" in; hij/zij voert immers 3.141592 in als "correct antwoord". Als hij/zij als antwoord 3.14 invoert zou je epsilon dan vanzelf 0.01 zijn. Of 0.001; net hoe je 't implementeert natuurlijk.

Het hele punt is wat je aan "foutmarge" accepteert. En wil je 0 foutmarge dan heb je gewoon überhaupt het verkeerde type (float) gekozen en zul je voor (bijv.) fixed integer of bcmath of andere exacte(re) types moeten gaan.

[ Voor 16% gewijzigd door RobIII op 19-04-2018 15:59 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
RobIII schreef op donderdag 19 april 2018 @ 15:56:
[...]
Wil je het "automatisch" doen: Je zou op basis van het (bekende) antwoord kunnen zeggen: er zijn 6 decimalen, dan gaan we (bijv.) op 6 decimalen vergelijken en is de epsilon dus 0.000001. Maar indirect voert de "leraar" / "examinator" / whatever dan toch nog een "epsilon" in; hij/zij voert immers 3.141592 in als "correct antwoord". Als hij/zij als antwoord 3.14 invoert zou je epsilon dan vanzelf 0.01 zijn. Of 0.001; net hoe je 't implementeert natuurlijk.
Volgens mij is dit de oplossing! Het docent-antwoord definieert de nauwkeurigheid. Thanks _/-\o_

Acties:
  • 0 Henk 'm!

  • ArnoutV
  • Registratie: December 2005
  • Laatst online: 18:59
Voor een generieke oplossing zou ik een (percentage) index gebruiken.
Hoeveel procent wijkt getal A (invoer) af van getal B (gegeven).
Dan is het een kwestie van afwijkingspercentages vast te leggen.

P = 100 * A / B
D = abs(100 - P)

Vaststellen wat goed is, bv afwijking van maximaal 1%
Als D < 1 dan OK

[ Voor 17% gewijzigd door ArnoutV op 19-04-2018 16:14 ]


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Rekcor schreef op donderdag 19 april 2018 @ 16:03:
[...]
Volgens mij is dit de oplossing! Het docent-antwoord definieert de nauwkeurigheid. Thanks _/-\o_
Mmm... te vroeg gejuicht. Bij het automatisch bepalen van het aantal decimalen van een float krijgen we hetzelfde probleem: bijv. 3.1415:

* óf we krijgen - in het geval van een puur mathematische bepaling als https://stackoverflow.com...f-a-number#answer-6619392 - opeens heel veel decimalen terug, omdat het stiekem 3.14149999999 is.

* óf we gaan 3.1415 toch naar een string moeten converteren, en dan het aantal karakters tellen na de punt.

Acties:
  • 0 Henk 'm!

  • emnich
  • Registratie: November 2012
  • Niet online

emnich

kom je hier vaker?

Rekcor schreef op donderdag 19 april 2018 @ 17:00:
[...]


Mmm... te vroeg gejuicht. Bij het automatisch bepalen van het aantal decimalen van een float krijgen we hetzelfde probleem: bijv. 3.1415:

* óf we krijgen - in het geval van een puur mathematische bepaling als https://stackoverflow.com...f-a-number#answer-6619392 - opeens heel veel decimalen terug, omdat het stiekem 3.14149999999 is.

* óf we gaan 3.1415 toch naar een string moeten converteren, en dan het aantal karakters tellen na de punt.
Rond je het eerst af?

Acties:
  • 0 Henk 'm!

  • GlowMouse
  • Registratie: November 2002
  • Niet online
ArnoutV schreef op donderdag 19 april 2018 @ 16:12:
Voor een generieke oplossing zou ik een (percentage) index gebruiken.
Hoeveel procent wijkt getal A (invoer) af van getal B (gegeven).
Dan is het een kwestie van afwijkingspercentages vast te leggen.

P = 100 * A / B
D = abs(100 - P)

Vaststellen wat goed is, bv afwijking van maximaal 1%
Als D < 1 dan OK
GlowMouse schreef op donderdag 19 april 2018 @ 14:10:
code:
1
Warning: Division by zero in Command line code on line 1

Acties:
  • 0 Henk 'm!

  • Blokker_1999
  • Registratie: Februari 2003
  • Laatst online: 16:49

Blokker_1999

Full steam ahead

Het grotere probleem is dat je daarmee terug bij af bent, want dan moet je opnieuw de max. toegestane afwijking kennen.

No keyboard detected. Press F1 to continue.


Acties:
  • +1 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Mijn poging om het dynamisch te doen:

PHP:
1
2
3
4
5
function equals($a, $b) {
    $digits = 3;
    $epsilon = max(abs($a), abs($b)) / pow(10, $digits);
    return abs($a - $b) <= $epsilon;
}

Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Daos schreef op donderdag 19 april 2018 @ 21:12:
Mijn poging om het dynamisch te doen:

PHP:
1
2
3
4
5
function equals($a, $b) {
    $digits = 3;
    $epsilon = max(abs($a), abs($b)) / pow(10, $digits);
    return abs($a - $b) <= $epsilon;
}
Maar $digits=3 is toch niet dynamisch? Bovendien begrijp ik je functie niet: $epsilon wordt bijna altijd gelijk aan $a ($a=1000000.2525 bijv.) en dan is het verschil tussen $a en $b al gauw kleiner dan $epsilon...

Acties:
  • 0 Henk 'm!

  • emnich
  • Registratie: November 2012
  • Niet online

emnich

kom je hier vaker?

De docent zal toch gewoon een getal invoeren als antwoord, of voert die zelf ook een berekening in?

Indien het laatste zou je kunnen kijken of er een getallenreeks na de komma herhaalt wordt. Dus als er minimaal 4 keer een 9 staat of 4 keer 925, rond je af en wordt dat je precisie.

Acties:
  • 0 Henk 'm!

Verwijderd

Het voorbeeld dat je geeft, is al fundamenteel fout. 58/4 is niet altijd hetzelfde als 116/8. Onder een zekere wiskundige bewerking die we op de lagere school hebben geleerd wel, maar onder de wiskundige bewerking van de / operator in php niet. Conclusie is dan ook onomkoombaar dat het vergelijken van breuken niet met het gebruik van floats kan.

Als je breuken wilt vergelijken zul je teller en noemer moeten ontbinden in priemfactoren, gelijke factoren moeten reduceren, en dan de tellers en noemers moeten vergelijken.

Acties:
  • 0 Henk 'm!

  • ValHallASW
  • Registratie: Februari 2003
  • Niet online
Rekcor schreef op donderdag 19 april 2018 @ 17:00:
Mmm... te vroeg gejuicht. Bij het automatisch bepalen van het aantal decimalen van een float krijgen we hetzelfde probleem: bijv. 3.1415:
Je moet niet het aantal decimalen van een float bepalen, maar het aantal decimalen in de string die de docent invoert als goed antwoord.

Acties:
  • 0 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Rekcor schreef op vrijdag 20 april 2018 @ 08:22:
[...]


Maar $digits=3 is toch niet dynamisch? Bovendien begrijp ik je functie niet: $epsilon wordt bijna altijd gelijk aan $a ($a=1000000.2525 bijv.) en dan is het verschil tussen $a en $b al gauw kleiner dan $epsilon...
Ik denk dat ik dan je probleem verkeerd begrepen heb. Je wilt geen vaste epsilon, dus denk ik, ik geef je een dynamische. Mijn functie zorgt ervoor dat bv 1.23456E10 gelijk is aan 1.23457E10, maar 1E-6 niet gelijk is aan 2E-6.

Acties:
  • 0 Henk 'm!

  • Bolukan
  • Registratie: Oktober 2002
  • Laatst online: 23-08 23:43
Daos schreef op donderdag 19 april 2018 @ 21:12:
Mijn poging om het dynamisch te doen:

PHP:
1
2
3
4
5
function equals($a, $b) {
    $digits = 3;
    $epsilon = max(abs($a), abs($b)) / pow(10, $digits);
    return abs($a - $b) <= $epsilon;
}
Bravo, de beste oplossing die de OP kan krijgen.

Het verschil vergelijken met een factor van het getal zelf. Hier kies je voor 1/10^3=1/1000, maar de OP kan ook 1/1.000.000ste nemen ($digits=6). De grootste - max - is hoewel ze waarschijnlijk gelijk zijn, is de veilige keuze.

Acties:
  • 0 Henk 'm!

  • Josk79
  • Registratie: September 2013
  • Laatst online: 05:25
Helaas gaat dat bij een resultaat rond de 0 ook niet altijd op:

code:
1
2
3
4
$a = 0.15 + 0.15;
$b = 0.10 + 0.20;

echo equals($a-$b, 0.0);

Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Daos schreef op vrijdag 20 april 2018 @ 19:55:
[...]

Ik denk dat ik dan je probleem verkeerd begrepen heb. Je wilt geen vaste epsilon, dus denk ik, ik geef je een dynamische. Mijn functie zorgt ervoor dat bv 1.23456E10 gelijk is aan 1.23457E10, maar 1E-6 niet gelijk is aan 2E-6.
Hoi! Ik zat denk ik te slapen vrijdag :) . Dank! Ik zal je oplossing eens loslaten in het wild.
Pagina: 1