Vinnaren i pepparkakshustävlingen!
  • 1
  • 2
2016-10-23, 13:08
  #1
Avstängd
En användare på Stack Overflow gav som ett skäl att inte använda flyttal för pengar, att "om man adderar ett öre hundra gånger är det inte säkert att man får en krona", men jag tror inte att det stämmer. Om flyttalstypen har en precision som lagrar 0.01 med t.ex. 1/2000 promilles felmarginal, kommer väl resultatet 1.00 (kronan) att ha 1/20 promilles felmarginal, och den marginalen får man även med konstanten 1 eftersom 1.00 har hundra gånger mer signifikans i siffror att lagra än 0.01?

Problemet med flyttal är väl bara den binära lagringen, inte operationen för den antar jag ger ett resultat som är exakt ända ner till hälften av den lägsta biten i mantissan. Ören lagrade som heltal blir mer inexakt än det om man använder division. Dela 100:- på tre arvtagare så får varje person bara 33,33:-. Så länge man inte jämför två flyttal med == eller andra relationsoperatorer, utan använder t.ex. `fabs(x - y) < 0.01`, och skriver ut endast två decimaler med `printf("%.2f", z)`, så borde flyttal vara bättre?

Affärer avrundar varje köp till en jämn krona. Om något kostar 9,90:- får jag inte tillbaka den där 10-öringen vid nästa köp. Det vore intressant att veta om bankerna gör likadant. Om man får en ränta på 123,456, som avrundas upp till 123,46, kommer man att få 123,45 nästa gång? Lagras saldot avrundat till exakta ören eller med maximal precision?

Kan inte avrundning till ören bli ett säkerhetshål? Om ICA även köpte produkter, skulle jag kunna gå in och köpa lagom antal sexpack folköl för 18,90:- styck så att avrundningen blev till min fördel, och sen sälja dem ett och ett för 19:-. Kan man göra något liknande med banktransaktioner om varje transaktion är gratis och går fort?
Citera
2016-10-23, 13:21
  #2
Medlem
min första tanke är att om du gör andra operationer, exempelvis division, kan du få variabler som innehåller fraktioner av ören. Om du då adderar sådana variabler kan du såklart hamna i lägen där du får nåt öre extra...

Detta kan vara önskvärt eller icke önskvärt beroende på situation

fördelen med att lagra i en integer ( förutom att det blir mindre tungdrivet) är att du tvingas trunkera hela tiden, men det gäller att tänka till så att du inte snor för många ören

Om du använder heltal bör man ju tänka till lite vid division så att man bevarar numerisk precision, men det är ju de flesta medvetna om.


allt handlar ju om avvägningar som vanligt...

precisionen är det inget fel på om du använder flyttal, det handlar mer om när du skall trunkera/avrunda.

Troligtvis gör man det i slutoperationen som presenteras mot kund som i ditt exempel med 100/3

kunden får då betala 33.33 hela 1/3 öre i rabatt
__________________
Senast redigerad av obisvenkanobi 2016-10-23 kl. 13:25.
Citera
2016-10-23, 13:24
  #3
Medlem
bithaxs avatar
Python dokumentationen ger ett bra exempel på varför det inte är en bra ide att räkna pengar med flyttal. Avrundningar blir t.ex. inte rätt.

https://docs.python.org/2/tutorial/floatingpoint.html
Citat:
round(2.675, 2)
2.67

The documentation for the built-in round() function says that it rounds to the nearest value, rounding ties away from zero. Since the decimal fraction 2.675 is exactly halfway between 2.67 and 2.68, you might expect the result here to be (a binary approximation to) 2.68. It’s not, because when the decimal string 2.675 is converted to a binary floating-point number, it’s again replaced with a binary approximation, whose exact value is

2.674999999999999822364316059974953532218933105468 75

I stället bör man använda det som i Java kallas för BigDecimal (tror decimal i python är samma sak). Sen skall man vara medveten om hur man hanterar avrundningar osv. att det är ett problem.

Det handlar om att vara smart och tänka efter i varje situation där man använder flyttal, inte att flyttal är förbjudna.

Från javadocen:

https://docs.oracle.com/javase/7/doc...igDecimal.html
Citat:
The BigDecimal class gives its user complete control over rounding behavior. If no rounding mode is specified and the exact result cannot be represented, an exception is thrown; otherwise, calculations can be carried out to a chosen precision and rounding mode by supplying an appropriate MathContext object to the operation. In either case, eight rounding modes are provided for the control of rounding. Using the integer fields in this class (such as ROUND_HALF_UP) to represent rounding mode is largely obsolete; the enumeration values of the RoundingMode enum, (such as RoundingMode.HALF_UP) should be used instead.
__________________
Senast redigerad av bithax 2016-10-23 kl. 13:37.
Citera
2016-10-23, 14:02
  #4
Avstängd
Citat:
Ursprungligen postat av bithax
Python dokumentationen ger ett bra exempel på varför det inte är en bra ide att räkna pengar med flyttal. Avrundningar blir t.ex. inte rätt.

Citat:
round(2.675, 2)
Resut: 2.67

The documentation for the built-in round() function says that it rounds to the nearest value, rounding ties away from zero. Since the decimal fraction 2.675 is exactly halfway between 2.67 and 2.68, you might expect the result here to be (a binary approximation to) 2.68. It’s not, because when the decimal string 2.675 is converted to a binary floating-point number, it’s again replaced with a binary approximation, whose exact value is

2.674999999999999822364316059974953532218933105468 75

Problemet ligger inte i avrundningsoperationen, utan i att man låter användaren skriva in (t.ex. en transaktion) med tre decimaler när man internt räknar bara med två. Inte bara output måste skyddas mot fler decimaler än två, utan även input, då borde det fungera. scanf() har ingen möjlighet att ange precision som printf(), så man lär parse:a strängen själv.
Citera
2016-10-23, 14:11
  #5
Medlem
bithaxs avatar
Citat:
Ursprungligen postat av SuperSizeMe
Problemet ligger inte i avrundningsoperationen, utan i att man låter användaren skriva in (t.ex. en transaktion) med tre decimaler när man internt räknar bara med två. Inte bara output måste skyddas mot fler decimaler än två, utan även input, då borde det fungera. scanf() har ingen möjlighet att ange precision som printf(), så man lär parse:a strängen själv.

Och du ser inte hur situationen skulle kunna uppstå att man får fler decimaler än två någon stans i beräkningarna man gör? Säg att du skall addera massor av olika varor i en korg, dra av en rabatt och beräkna 25% moms, det blir ju garanterat inte rätt på sista raden.

Jag tycker det är en farlig strategi. Bättre att använda sig av en datastruktur som hanterar decimaler och avrundning på det sätt man vill i stället.
__________________
Senast redigerad av bithax 2016-10-23 kl. 14:17.
Citera
2016-10-23, 14:28
  #6
Avstängd
Citat:
Ursprungligen postat av bithax
Och du ser inte hur situationen skulle kunna uppstå att man får fler decimaler än två någonstans i beräkningarna man gör?

Jo, men att få exakt 2,675(oändligt antal nollor här):- efter ränta på ränta och en massa divisioner, kommer aldrig att hända, om du inte lagrar saldot med ett oändligt stort heltal som täljare och ett oändligt stort heltal som nämnare.

Citat:
Ursprungligen postat av bithax
Jag tycker det är en farlig strategi. Bättre att använda sig av en datastruktur som hanterar decimaler och avrundning på det sätt man vill i stället.

Jag tycker det vore bäst om banken alltid lagrade mitt saldo så exakt som möjligt, in till minsta bit, och bara avrundade det som visas på skärmen (eller kommer in som input). Alltså undvika avrundning internt så mycket som möjligt, vilket är precis vad flyttalsoperationer gör.

Farligt kommer det att bli först när man blir miljonär och precisionen inte räcker till, då kan man tjäna ett öre per transaktion, men även med heltal måste man ju ha koll på overflow.
__________________
Senast redigerad av SuperSizeMe 2016-10-23 kl. 14:30.
Citera
2016-10-23, 14:43
  #7
Medlem
Brunbeverns avatar
Jag skulle kunna tänka mig att det beror mer eller mindre på att man inte kan representera 0,1 i binärt på ett bra sätt. Tänk 1/3 med vårat talsystem, det är ju 0,33333.. med oändligt många 3or.

Fast helklart en intressant fråga. Tror det är klokt att lyssna på rådet om att undvika flyttal när man hanterar pengar, men visst vore det intressant att förstå till 100% varför. Fast utgår man från det första, så förklarar ju det varför exempelvis följande kod inte ger "väntat" resultat:

Kod:
public class Main {
  public static void main(String[] args) {
    double a = 0.7;
    double b = 0.9;
    double x = a + 0.1;
    double y = b - 0.1;
    System.out.println(x == y);
  }
}
(http://stackoverflow.com/questions/9...ounding-errors)
Citera
2016-10-23, 18:41
  #8
Medlem
bithaxs avatar
Citat:
Ursprungligen postat av SuperSizeMe
Jag tycker det vore bäst om banken alltid lagrade mitt saldo så exakt som möjligt, in till minsta bit, och bara avrundade det som visas på skärmen (eller kommer in som input). Alltså undvika avrundning internt så mycket som möjligt, vilket är precis vad flyttalsoperationer gör.

Farligt kommer det att bli först när man blir miljonär och precisionen inte räcker till, då kan man tjäna ett öre per transaktion, men även med heltal måste man ju ha koll på overflow.

Du har ju tydligen inte förstått ett skit av det jag har sagt. Hur har ett 32 bitars flyttal bättre precision än en dynamiskt allokerad struktur med konfigurerbar precision som BigDecimal? Med en float så är det ju så att ju pengar du har på kontot, desto sämre precision får du på dina ören. Värdelöst!

Banken lagrar förmodligen sin data som DECIMAL i SQL, inte som en FLOAT.
__________________
Senast redigerad av bithax 2016-10-23 kl. 18:43.
Citera
2016-10-23, 22:19
  #9
Avstängd
Citat:
Ursprungligen postat av bithax
Du har ju tydligen inte förstått ett skit av det jag har sagt. Hur har ett 32 bitars flyttal bättre precision än en dynamiskt allokerad struktur med konfigurerbar precision som BigDecimal? Med en float så är det ju så att ju mer pengar du har på kontot, desto sämre precision får du på dina ören. Värdelöst!

Jodå, jag fattar, spånar bara lite Sätter jag in 20 öre (0.20) vill jag ju vara rik om några tusen år, inte få en avrundad ränta på 0.00 varje år.

Visst kan det vara jobbigt för en rik person att "slumpmässigt" förlora eller vinna ett extra öre (ironi), men ett bignum som lagrar och avrundar allt till ören, är värre. Då blir det istället att ju fattigare man är, desto mer kan man "slumpmässigt" vinna/förlora relativt mot sitt saldo.

Ett problem med flyttal som lagrade med mer precison än vad som skrivs ut, skulle dock vara att man inte kan räkna dem för hand. En revisor kan räkna ihop en massa poster som har skrivits ut med bara två decimalers noggrannhet, och få ett annat resultat än den egentliga summan.
Citera
2016-10-23, 22:25
  #10
Medlem
bithaxs avatar
Citat:
Ursprungligen postat av SuperSizeMe
Jodå, jag fattar, spånar bara lite Sätter jag in 20 öre (0.20) vill jag ju vara rik om några tusen år, inte få en avrundad ränta på 0.00 varje år.

Visst kan det vara jobbigt för en rik person att "slumpmässigt" förlora eller vinna ett extra öre (ironi), men ett bignum som lagrar och avrundar allt till ören, är värre. Då blir det istället att ju fattigare man är, desto mer kan man "slumpmässigt" vinna/förlora relativt mot sitt saldo.

Ett problem med flyttal som lagrade med mer precison än vad som skrivs ut, skulle dock vara att man inte kan räkna dem för hand. En revisor kan räkna ihop en massa poster som har skrivits ut med bara två decimalers noggrannhet, och få ett annat resultat än den egentliga summan.

Tror mest du har autism och måste ha rätt i din argumentation att flyttal är bättre till allt för att du är en C++ nörd. Så gör din grej, jag skiter i vilket.
Citera
2016-10-23, 23:01
  #11
Avstängd
Citat:
Ursprungligen postat av bithax
Tror mest du har autism och måste ha rätt i din argumentation att flyttal är bättre till allt för att du är en C++ nörd. Så gör din grej, jag skiter i vilket.

Bra. Om du inte är intresserad av att argumentera för- mot nackdelar kan du lämna tråden. Personliga påhopp, dessutom falska (läs mitt sista inlägg igen), tillför inget.
Citera
2016-10-24, 06:30
  #12
Medlem
MeanMEs avatar
Citat:
Ursprungligen postat av SuperSizeMe
En användare på Stack Overflow gav som ett skäl att inte använda flyttal för pengar, att "om man adderar ett öre hundra gånger är det inte säkert att man får en krona", men jag tror inte att det stämmer. Om flyttalstypen har en precision som lagrar 0.01 med t.ex. 1/2000 promilles felmarginal, kommer väl resultatet 1.00 (kronan) att ha 1/20 promilles felmarginal, och den marginalen får man även med konstanten 1 eftersom 1.00 har hundra gånger mer signifikans i siffror att lagra än 0.01?

Problemet med flyttal är väl bara den binära lagringen, inte operationen för den antar jag ger ett resultat som är exakt ända ner till hälften av den lägsta biten i mantissan. Ören lagrade som heltal blir mer inexakt än det om man använder division. Dela 100:- på tre arvtagare så får varje person bara 33,33:-. Så länge man inte jämför två flyttal med == eller andra relationsoperatorer, utan använder t.ex. `fabs(x - y) < 0.01`, och skriver ut endast två decimaler med `printf("%.2f", z)`, så borde flyttal vara bättre?

Affärer avrundar varje köp till en jämn krona. Om något kostar 9,90:- får jag inte tillbaka den där 10-öringen vid nästa köp. Det vore intressant att veta om bankerna gör likadant. Om man får en ränta på 123,456, som avrundas upp till 123,46, kommer man att få 123,45 nästa gång? Lagras saldot avrundat till exakta ören eller med maximal precision?

Kan inte avrundning till ören bli ett säkerhetshål? Om ICA även köpte produkter, skulle jag kunna gå in och köpa lagom antal sexpack folköl för 18,90:- styck så att avrundningen blev till min fördel, och sen sälja dem ett och ett för 19:-. Kan man göra något liknande med banktransaktioner om varje transaktion är gratis och går fort?
Han har troligen rätt'ish'.
Det är bättre att lagra pengar som heltal.
Enklast gör du som så att i ett öresbaserat system som i Sverige så blir 45.34, 4534 osv.
Då slipper du all problematik med flyttal och evt jämförelser du kan behöva göra.
Avrundning, konvertering mm sker vid presentationen inte i beräkningar och vid lagring.
Är det räntesatser kanske du behöver multiplicera med 1000 då ränta oftast ges i decimalform eg 7.5% ränta osv. Flyttal är ett jävla mög imho att hålla på med ibland.
Citera
  • 1
  • 2

Stöd Flashback

Flashback finansieras genom donationer från våra medlemmar och besökare. Det är med hjälp av dig vi kan fortsätta erbjuda en fri samhällsdebatt. Tack för ditt stöd!

Stöd Flashback