Wat is de maximale lengte in tekens die nodig is om een ​​dubbele waarde weer te geven?

Als ik een niet-ondertekende 8-bits int converteer naar een string, dan weet ik dat het resultaat altijd maximaal 3 tekens zal zijn (voor 255) en voor een ondertekende 8-bits int hebben we 4 tekens nodig voor b.v. “-128”.

Wat ik me eigenlijk afvraag is hetzelfde voor waarden met drijvende komma. Wat is het maximale aantal tekens dat nodig is om een ​​”dubbele” of “zwevende” waarde als tekenreeks weer te geven?

Veronderstel een normale C/C++ double (IEEE 754) en normale decimale expansie (d.w.z. geen %e printf-formattering).

Ik weet niet eens zeker of het echt kleine getal (d.w.z. 0.234234) langer zal zijn dan de echt enorme getallen (dubbele getallen vertegenwoordigen gehele getallen)?


Antwoord 1, autoriteit 100%

De standaard header <float.h>in C, of ​​<cfloat>in C++, bevat verschillende constanten die te maken hebben met het bereik en andere metrieken van de drijvende-komma typen. Een daarvan is DBL_MAX_10_EXP, de grootste macht-van-10 exponent die nodig is om alle doublewaarden weer te geven. Aangezien 1eNN+1cijfers nodig heeft om weer te geven, en er kan ook een negatief teken zijn, dan is het antwoord

int max_digits = DBL_MAX_10_EXP + 2;

Hierbij wordt aangenomen dat de exponent groter is dan het aantal cijfers dat nodig is om de grootst mogelijke mantissewaarde weer te geven; anders zal er ook een decimaalteken zijn gevolgd door meer cijfers.

CORRECTIE

Het langste getal is eigenlijk het kleinste representeerbare negatieve getal: het heeft voldoende cijfers nodig om zowel de exponent als de mantisse te dekken. Deze waarde is -pow(2, DBL_MIN_EXP - DBL_MANT_DIG), waarbij DBL_MIN_EXPnegatief is. Het is vrij eenvoudig in te zien (en door inductie te bewijzen) dat -pow(2,-N)3+Ntekens nodig heeft voor een niet-wetenschappelijke decimale representatie ("-0.", gevolgd door Ncijfers). Dus het antwoord is

int max_digits = 3 + DBL_MANT_DIG - DBL_MIN_EXP

Voor een 64-bits IEEE double hebben we

DBL_MANT_DIG = 53
DBL_MIN_EXP = -1023
max_digits = 3 + 53 - (-1023) = 1079

Antwoord 2, autoriteit 50%

Volgens IEEE 754-1985, de langste notatie voor waarde vertegenwoordigd door dubbel type, dat wil zeggen:

-2.2250738585072020E-308

heeft 24 tekens.


Antwoord 3, autoriteit 15%

Een correcte informatiebron die dieper ingaat op de IEEE-754-specificatiezijn deze lezingen van UC Berkelyop pagina 4, plus wat doe-het-zelfberekeningen. Deze collegedia’szijn ook goed voor technische studenten.

Aanbevolen buffergroottes

| Single| Double | Extended | Quad  |
|:-----:|:------:|:--------:|:-----:|
|   16  |  24    |    30    |  45   |

Deze cijfers zijn gebaseerd op de volgende berekeningen:

Maximale decimale telling van het integrale deel

| Single| Double | Extended | Quad  |
|:-----:|:------:|:--------:|:-----:|
|   9   |   17   |    21    |  36   |
* Quantities listed in decimals.

Decimale tellingen zijn gebaseerd op de formule: Maximaal Plafond(1 + NLog_10(2)) decimalen, waarbij N het aantal bits in het integrale deel* is.

Maximale lengten van exponenten

| Single| Double | Extended | Quad  |
|:-----:|:------:|:--------:|:-----:|
|   5   |   5    |     7    |   7   |
* Standard format is `e-123`.

Snelste algoritme

Het snelste algoritme voor het afdrukken van getallen met drijvende komma is het Grisu2-algoritme dat wordt beschreven in de onderzoekspaper Drijvende-kommagetallen snel en nauwkeurig afdrukken. De beste benchmark die ik kon vinden, vind je hier.


Antwoord 4, autoriteit 10%

Je kunt snprintf()om te controleren hoeveel tekens je nodig hebt.
snprintf()geeft het aantal tekens terug dat nodig is om alles af te drukken wat eraan wordt doorgegeven.

/* NOT TESTED */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
    char dummy[1];
    double value = 42.000042; /* or anything else */
    int siz;
    char *representation;
    siz = snprintf(dummy, sizeof dummy, "%f", value);
    printf("exact length needed to represent 'value' "
           "(without the '\\0' terminator) is %d.\n", siz);
    representation = malloc(siz + 1);
    if (representation) {
        sprintf(representation, "%f", value);
        /* use `representation` */
        free(representation);
    } else {
        /* no memory */
    }
    return 0;
}

Opmerking: snprintf()is een C99-functie. Als een C89-compiler het als een extensie levert, doet het mogelijk niet wat het bovenstaande programma verwacht.

Bewerken:
De link naar snprintf()gewijzigd in een link die de functionaliteit beschrijft die wordt opgelegd door de C99-standaard; de beschrijving in de originele linkis onjuist.
2013: de link teruggewijzigd naar de POSIX-site die ik verkies boven de site van de eerste bewerking.


Antwoord 5, autoriteit 5%

“Wat is de maximale lengte in tekens die nodig is om een ​​dubbele waarde weer te geven?”

Het exacte antwoord op deze vraag is: 8 ASCII-tekens – in hexadicimaal formaat, exclusief het voorvoegsel ‘0x’ – 100% nauwkeurigheid 🙂 (maar het is niet zomaar een grap)

De bruikbare precisie van IEEE-754 double is ongeveer 16 cijfers achter de komma – dus educatieve doeleinden buiten beschouwing gelaten, zijn representaties die langer zijn gewoon een verspilling van middelen en rekenkracht:

  • Gebruikers worden niet beter geïnformeerd als ze een 700-cijferig nummer op het scherm zien.

  • Configuratievariabelen die in die “nauwkeurigere” vorm zijn opgeslagen, zijn nutteloos – elke bewerking op zo’n nummer vernietigt de nauwkeurigheid. (exclusief het wijzigen van het tekenbit)

Als iemand een betere echteprecisie nodig heeft, dan is er een 80-bit lange double met een nauwkeurigheid van ongeveer 18 cijfers of bijv. libquadmath.

Met vriendelijke groet.


Antwoord 6, autoriteit 2%

Afhankelijk van wat je bedoelt met “vertegenwoordigen”. Decimale breuken hebben geen exacte drijvende-komma-representaties. Wanneer u decimale breuken converteert -> binaire breuk -> decimaal, je hebt geen exacte decimale representaties en ruisbits aan het einde van de binaire representatie.

De vraag ging niet over het beginnen met decimaal, maar alle broncode (en moet door de gebruiker worden ingevoerd) is decimaal en heeft betrekking op de mogelijke afknotting. Wat betekent “exact” onder deze omstandigheden?

In principe hangt het af van uw drijvende-kommaweergave.

Als je 48 bits mantisse hebt, duurt dit ongeveer 16 cijfers achter de komma. De exponent kan de resterende 14 bits zijn (ongeveer 5 cijfers achter de komma).

De vuistregel is dat het aantal bits ongeveer 3x het aantal decimale cijfers is.


Antwoord 7, autoriteit 2%

U kunt het aantal cijfers in de tekenreeksweergave bepalen wanneer u de float/double naar een tekenreeks converteert door de precisie in te stellen. Het maximum aantal cijfers zou dan gelijk zijn aan de tekenreeksrepresentatie van std::numeric_limits<double>::max()met de precisie die u opgeeft.

#include <iostream>
#include <limits>
#include <sstream>
#include <iomanip>
int main()
{
 double x = std::numeric_limits<double>::max();
 std::stringstream ss;
 ss << std::setprecision(10) << std::fixed << x;
 std::string double_as_string = ss.str();
 std::cout << double_as_string.length() << std::endl;
}

Dus het grootste aantal cijfers in een doublemet een precisie van 10 is 320 cijfers.


Antwoord 8, autoriteit 2%

1024 is niet genoeg, de kleinste negatieve dubbele waarde heeft 1077 decimale cijfers. Hier is wat Java-code.

double x = Double.longBitsToDouble(0x8000000000000001L);
BigDecimal bd = new BigDecimal(x);
String s = bd.toPlainString();
System.out.println(s.length());
System.out.println(s);

Hier is de uitvoer van het programma.

1077
-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625

Antwoord 9

Het maximum aantal tekens dat nodig is om een ​​decimale double-waarde af te drukken (dwz in de indeling "%f") is voor de waarde van -DBL_MIN(dwz -0x1p-1022, ervan uitgaande dat binary64 IEEE 754 uw doubleis). Daarvoor heb je precies 325 tekens nodig. Dat is: DBL_DIG + abs(DBL_MIN_10_EXP) + strlen("-0."). Dit komt natuurlijk omdat log10(fabs(DBL_MIN))308 is, wat ook abs(DBL_MIN_10_EXP)+1is (de +1 is vanwege het eerste cijfer van de links van de decimale plaats), en dat is het aantal voorloopnullen links van de significante cijfers.

int lz;                 /* aka abs(DBL_MIN_10_EXP)+1 */
int dplaces;
int sigdig;             /* aka DBL_DECIMAL_DIG - 1 */
double dbl = -DBL_MIN;
lz = abs((int) lrint(floor(log10(fabs(dbl)))));
sigdig = lrint(ceil(DBL_MANT_DIG * log10((double) FLT_RADIX)));
dplaces = sigdig + lz - 1;
printf("f = %.*f\n", dplaces, dbl);

Antwoord 10

“Daarvoor heb je precies 325 tekens nodig”

Blijkbaar (en dit is een veel voorkomend geval) begrijpt u niet hoe de conversie tussen verschillende numerieke basen werkt.

Hoe nauwkeurig de definitie van de DBL_MIN ook is, deze wordt beperkt door de nauwkeurigheid van de hardware, die gewoonlijk tot 80 bits of 18 decimale cijfers (x86 en vergelijkbare architecturen) bedraagt

Om die reden zijn er gespecialiseerde bibliotheken met willekeurige precisie-rekenkunde uitgevonden, zoals b.v. gmp of mpfr.


Antwoord 11

Als verbetering van het geaccepteerde antwoordop basis van Nauwkeurige opmerking van Greg A. Woods, een conservatiever maar nog steeds adequaat aantal aantal tekens nodig is 3 + DBL_DIG + -DBL_MIN_10_EXP(totaal 325) waarbij 3 voor de eerste “-0” is. dat kan nodig zijn. Als u strings in C-stijl gebruikt, voegt u er een toe voor null-beëindiging ('\0') zodat een buffer van voldoende grootte (van grootte 326) kan worden gemaakt met:

#include <limits.h>
char buffer[4 + DBL_DIG + -DBL_MIN_10_EXP];

Voor degenen die de voorkeur geven aan de C++ numerieke limieteninterfacezou dat zijn:

#include <limits>
char buffer[4 + std::numeric_limits<double>::digits10 + -std::numeric_limits<double>::min_exponent10];

Other episodes