sql >> Database teknologi >  >> RDS >> Mysql

Bedste praksis til lagring af vægte i en SQL-database?

Du hævder, at der er iboende unøjagtigheder i flydende kommatal. Jeg synes, at det her fortjener at blive udforsket lidt først.

Når du beslutter dig for et talsystem for at repræsentere et tal (hvad enten det er på et stykke papir, i et computerkredsløb eller andre steder), er der to adskilte spørgsmål at overveje:

  1. dens grundlag; og

  2. dens format .

Vælg en base, en hvilken som helst base...

Begrænset af begrænset plads kan man ikke repræsentere et vilkårligt medlem af et uendeligt sæt . For eksempel:uanset hvor meget papir du køber eller hvor lille din håndskrift, ville det altid være muligt at finde et heltal, der ikke passer ind i det givne rum (du kan bare blive ved med at tilføje ekstra cifre, indtil papiret løber tør). Altså med heltal , begrænser vi normalt vores begrænsede rum til kun at repræsentere dem, der falder inden for et bestemt interval - f.eks. hvis vi har plads til det positive/negative fortegn og tre cifre, kan vi begrænse os til intervallet [-999,+999] .

Hver ikke-tomt interval indeholder et uendeligt sæt reelle tal. Med andre ord, uanset hvilket interval man overtager de reelle tal — det være sig [-999,+999] , [0,1] , [0.000001,0.000002] eller noget andet - der er stadig et uendeligt sæt af reelle værdier inden for det interval (man behøver kun at blive ved med at tilføje (ikke-nul) brøktal)! Derfor skal vilkårlige reelle tal altid være "afrundet" til noget, der kan være repræsenteret i begrænset rum.

Sættet af reelle tal, der kan repræsenteres i endeligt rum, afhænger af det talsystem, der bruges. I vores (velkendte) positionelle base-10 system, vil begrænset plads være tilstrækkelig til halvdelen (>0.510 ) men ikke for en tredjedel (0.33333…10 ); derimod i den (mindre velkendte) positionelle base-9 system, er det omvendt (de samme tal er henholdsvis 0.44444…9 og 0.39 ). Konsekvensen af ​​alt dette er, at nogle tal, der kan repræsenteres ved kun at bruge en lille mængde plads i positionsbase-10 (og derfor optræder at være meget "rund" for os mennesker), f.eks. en tiendedel, ville faktisk kræve uendelige binære kredsløb for at blive lagret præcist (og derfor ikke ser ud til at være meget "runde" for vores digitale venner)! Navnlig, da 2 er en faktor på 10, er det samme ikke sandt omvendt:ethvert tal, der kan repræsenteres med endelig binær, kan også repræsenteres med endelig decimal.

Vi kan ikke gøre det bedre for kontinuerlige mængder. I sidste ende skal sådanne mængder bruge en endelig repræsentation i nogle numeral system:det er vilkårligt, om det system tilfældigvis er let på computerkredsløb, på menneskelige fingre, på noget andet eller slet ingenting – uanset hvilket system der bruges, skal værdien være afrundet og derfor altid resulterer i "repræsentationsfejl".

Med andre ord, selvom man har et perfekt nøjagtigt måleinstrument (hvilket er fysisk umuligt), så vil enhver måling, den rapporterer, allerede være blevet afrundet til et tal, der tilfældigvis passer på displayet (uanset hvilken base det bruger – typisk decimal, af indlysende årsager). Så "86.2 oz" er faktisk aldrig "86.2 oz " men snarere en repræsentation af "noget mellem 86.1500000... oz og 86.2499999... oz ". (Faktisk, fordi instrumentet i virkeligheden er ufuldkomment, kan vi kun sige, at vi har nogle grad af tillid at den faktiske værdi falder inden for det interval – men det afviger bestemt et stykke fra punktet her).

Men vi kan gøre det bedre for diskrete mængder . Sådanne værdier er ikke "vilkårlige reelle tal", og derfor gælder intet af ovenstående for dem:de kan repræsenteres præcist i det talsystem, som de blev defineret i – og faktisk bør være (da konvertering til et andet talsystem og afkortning til en endelig længde ville resultere i afrunding til et upræcis tal). Computere kan (ineffektivt) håndtere sådanne situationer ved at repræsentere tallet som en streng:f.eks. overvej ASCII eller BCD kodning.

Anvend et format...

Da det er en egenskab på talsystemets (noget vilkårlige) grundlag, har om en værdi ser ud til at være "rund" eller ej nogen betydning for dens præcision . Det er en virkelig vigtig observation , hvilket er i modstrid med mange menneskers intuition (og det er grunden til, at jeg brugte så meget tid på at forklare det numeriske grundlag ovenfor).

Præcision bestemmes i stedet af hvor mange signifikante tal en repræsentation har . Vi har brug for et lagerformat, der er i stand til at registrere vores værdier til mindst så mange væsentlige tal som vi anser dem for at være korrekte . Som eksempel på værdier, som vi anser for at være korrekte, når de er angivet som 86.2 og 0.0000862 , de to mest almindelige muligheder er:

  • Fast punkt , hvor antallet af signifikante tal afhænger af størrelsesorden :for eksempel. i fast 5-decimal-point repræsentation vil vores værdier blive gemt som 86.20000 og 0.00009 (og har derfor henholdsvis 7 og 1 signifikante præcisionstal). I dette eksempel er præcisionen gået tabt i sidstnævnte værdi (og der skulle faktisk ikke meget mere til, før vi var fuldstændig ude af stand til at repræsentere noget af betydning); og den tidligere værdi gemt falsk præcision , hvilket er spild af vores begrænsede plads (og der skal faktisk ikke meget mere til, før værdien bliver så stor, at den løber over lagerkapaciteten).

    Et almindeligt eksempel på, hvornår dette format kan være passende, er for et regnskabssystem:pengebeløb skal normalt spores til øret uafhængigt af deres størrelse (derfor kræves mindre præcision for små værdier, og mere præcision kræves for store værdier). Som det sker, anses valuta normalt også for at være diskret (pennies er udelelige), så dette er også et godt eksempel på en situation, hvor et bestemt grundlag (decimal for de fleste moderne valutaer) er ønskeligt for at undgå repræsentationsfejlene diskuteret ovenfor.

  • Flydende komma , hvor antallet af signifikante tal er konstant uanset størrelse :for eksempel. i 5-signifikant-cifret decimalrepræsentation vil vores værdier blive gemt som 86.200 og 0.000086200 (og pr. definition har 5 signifikante præcisionstal begge gange). I dette eksempel er begge værdier blevet gemt uden tab af præcision; og de har begge også det samme beløb af falsk præcision, hvilket er mindre spild (og vi kan derfor bruge vores begrænsede rum til at repræsentere et langt større udvalg af værdier – både store og små).

    Et almindeligt eksempel på, hvornår dette format kan være passende, er til registrering af målinger fra den virkelige verden :præcisionen af ​​måleinstrumenter (som alle lider af både systematisk og tilfældig fejl) er nogenlunde konstant uanset skala, så givet tilstrækkeligt signifikante tal (typisk omkring 3 eller 4 cifre), går absolut ingen præcision tabt selvom en ændring af grundtallet resulterede i afrunding til et andet tal .

    Men hvor præcise er lagringsformaterne med flydende komma bruges af vores computere?

    • En IEEE754 single precision (binary32) floating point nummer har 24 bit, eller log10(2) (over 7) cifre, af betydning - dvs. den har en tolerance på mindre end ±0.000006% . Det er med andre ord mere præcist end at sige "86.20000 ".

    • En IEEE754 dobbelt præcision (binary64) flydende komma nummer har 53 bit, eller log10(2) (næsten 16) cifre, af betydning - dvs. den har en tolerance på lidt over ±0.00000000000001% . Det er med andre ord mere præcist end at sige "86.2000000000000 ".

    Det vigtigste at indse er, at disse formater er henholdsvis over ti tusinde og over en billion gange mere præcis end at sige "86.2" – selvom nøjagtige konverteringer af binæren tilbage til decimal tilfældigvis inkluderer fejlagtig falsk præcision (som vi må ignorere:mere om dette snart)!

Bemærk også, at begge dele rettet og flydende kommaformater vil resultere i tab af præcision, når en værdi er kendt mere præcist, end formatet understøtter. Sådanne afrundingsfejl kan forplante sig i aritmetiske operationer for at give tilsyneladende fejlagtige resultater (hvilket uden tvivl forklarer din henvisning til de "iboende unøjagtigheder" af flydende kommatal):for eksempel 3 × 3000 i 5-pladser fikspunkt ville give 999.99000 i stedet for 1000.00000; og 7 − ⁄50 i 5-signifikante tal ville flydende komma give 0.0028600 i stedet for 0.0028571 .

Feltet numerisk analyse er dedikeret til at forstå disse effekter, men det er vigtigt at indse, at enhver brugbart system (selv udfører beregninger i dit hoved) er sårbart over for sådanne problemer, fordi ingen beregningsmetode, der med garanti vil afslutte, nogensinde kan tilbyde uendelig præcision :overvej f.eks., hvordan man beregner arealet af en cirkel – der vil nødvendigvis være præcisionstab i den værdi, der bruges til π, hvilket vil forplante sig til resultatet.

Konklusion

  1. Målinger i den virkelige verden bør bruge binært flydende komma :det er hurtigt, kompakt, ekstremt præcist og ikke værre end noget andet (inklusive decimalversionen, hvorfra du startede). Siden MySQL's floating-point-datatyper er IEEE754, det er præcis, hvad de tilbyder.

  2. Valutaapplikationer skal bruge denary fix point :Selvom det er langsomt og spilder hukommelse, sikrer det både, at værdier ikke afrundes til upræcise mængder, og at øre ikke går tabt på store pengesummer. Siden MySQL's fastpunktsdatatyper er BCD-kodede strenge, det er præcis, hvad de tilbyder.

Husk endelig på, at programmeringssprog normalt repræsenterer brøkværdier ved brug af binært flydende komma typer:så hvis din database gemmer værdier i et andet format, skal du være forsigtig med, hvordan de bringes ind i din applikation, ellers kan de blive konverteret (med alle de deraf følgende problemer) ved grænsefladen.

Hvilken mulighed er bedst i dette tilfælde?

Forhåbentlig har jeg overbevist dig om, at dine værdier sikkert kan (og bør). ) opbevares i flydende kommatyper uden at bekymre dig for meget om eventuelle "unøjagtigheder"? Husk, de er flere præcis end din spinkle 3-signifikant-cifrede decimalrepræsentation nogensinde var:du skal bare ignorere falsk præcision (men man skal altid gør det alligevel, selvom du bruger et fast punktdecimalformat).

Med hensyn til dit spørgsmål:vælg enten mulighed 1 eller 2 frem for mulighed 3 - det gør sammenligninger nemmere (for eksempel for at finde den maksimale masse, kunne man bare bruge MAX(mass) , hvorimod at gøre det effektivt på tværs af to kolonner ville kræve noget indlejring).

Mellem disse to er det ligegyldigt, hvilken man vælger – flydende kommatal gemmes med et konstant antal signifikante bits uanset deres skala .

Desuden, mens det i det generelle tilfælde kan ske, at nogle værdier afrundes til binære tal, der er tættere på deres oprindelige decimalrepræsentation ved brug af mulighed 1, mens andre samtidigt afrundes til binære tal, der er tættere på deres oprindelige decimalrepræsentation ved hjælp af mulighed 2, som vi vil snart se sådanne repræsentationsfejl kun manifestere sig inden for den falske præcision, som altid bør ignoreres.

Men i denne Fordi det sker, at der er 16 ounce til 1 pund (og 16 er en potens af 2), er de relative forskelle mellem originale decimalværdier og lagrede binære tal ved brug af de to tilgange identiske :

  1. 5.387510 (ikke 5.3367187510 som angivet i dit spørgsmål) vil blive gemt i en binary32 float som 101.0110001100110011001102 (som er 5.3874998092651367187510 ):dette er 0.0000036% fra den oprindelige værdi (men, som diskuteret ovenfor, var den "oprindelige værdi" allerede en ret elendig repræsentation af den fysiske mængde, den repræsenterer).

    Ved at vide, at en binary32-float kun lagrer 7 decimalcifre af præcision, ved vores compiler med sikkerhed at alt fra 8. ciffer og frem er bestemt falsk præcision og derfor skal ignoreres i hver case – således forudsat at vores inputværdi ikke krævede mere præcision end det (og hvis det gjorde, var binary32 åbenbart det forkerte valg af format), dette garanti en tilbagevenden til en decimalværdi, der ser lige så rund ud som den, vi startede fra:5.38750010 . Men vi bør virkelig anvende domæneviden på dette tidspunkt (som vi burde med ethvert lagerformat) for at kassere enhver yderligere falsk præcision, der måtte eksistere, såsom de to efterfølgende nuller.

  2. 86.210 ville blive gemt i en binær 32 float som 1010110.001100110011001102 (som er 86.199996948242187510 ):dette er også 0.0000036% fra den oprindelige værdi. Som før ignorerer vi så falsk præcision for at vende tilbage til vores oprindelige input.

Læg mærke til, hvordan de binære repræsentationer af tallene er identiske, bortset fra placeringen af ​​radixpunktet (som er fire bits fra hinanden):

101.0110 00110011001100110
101 0110.00110011001100110

Dette skyldes, at 5,3875 × 2 =86,2.



  1. Sådan finder du tabeller, der indeholder en specifik kolonne i SQL Server

  2. Opdater forespørgsel med PDO og MySQL

  3. php/mysql. Er der mysql-funktion til at opdage, om der findes mindst 2 bestemte ord i mindst ét ​​mysql-rækkefelt

  4. Ordliste over SQL Server-forespørgsler — A Stick Shift for DBA'er