C/C++ counting the number of decimals? - c++

Lets say that input from the user is a decimal number, ex. 5.2155 (having 4 decimal digits). It can be stored freely (int,double) etc.
Is there any clever (or very simple) way to find out how many decimals the number has? (kinda like the question how do you find that a number is even or odd by masking last bit).

Two ways I know of, neither very clever unfortunately but this is more a limitation of the environment rather than me :-)
The first is to sprintf the number to a big buffer with a "%.50f" format string, strip off the trailing zeros then count the characters after the decimal point. This will be limited by the printf family itself. Or you could use the string as input by the user (rather than sprintfing a floating point value), so as to avoid floating point problems altogether.
The second is to subtract the integer portion then iteratively multiply by 10 and again subtract the integer portion until you get zero. This is limited by the limits of computer representation of floating point numbers - at each stage you may get the problem of a number that cannot be represented exactly (so .2155 may actually be .215499999998). Something like the following (untested, except in my head, which is about on par with a COMX-35):
count = 0
num = abs(num)
num = num - int(num)
while num != 0:
num = num * 10
count = count + 1
num = num - int(num)
If you know the sort of numbers you'll get (e.g., they'll all be 0 to 4 digits after the decimal point), you can use standard floating point "tricks" to do it properly. For example, instead of:
while num != 0:
use
while abs(num) >= 0.0000001:

Once the number is converted from the user representation (string, OCR-ed gif file, whatever) into a floating point number, you are not dealing with the same number necessarily. So the strict, not very useful answer is "No".
If (case A) you can avoid converting the number from the string representation, the problem becomes much easier, you only need to count the digits after the decimal point and subtract the number of trailing zeros.
If you cannot do it (case B), then you need to make an assumption about the maximum number of decimals, convert the number back into string representation and round it to this maximum number using the round-to-even method. For example, if the user supplies 1.1 which gets represented as 1.09999999999999 (hypothetically), converting it back to string yields, guess what, "1.09999999999999". Rounding this number to, say, four decimal points gives you "1.1000". Now it's back to case A.

Off the top of my head:
start with the fractional portion: .2155
repeatedly multiply by 10 and throw away the integer portion of the number until you get zero. The number of steps will be the number of decimals. e.g:
.2155 * 10 = 2.155
.155 * 10 = 1.55
.55 * 10 = 5.5
.5 * 10 = 5.0
4 steps = 4 decimal digits

Something like this might work as well:
float i = 5.2154;
std::string s;
std::string t;
std::stringstream out;
out << i;
s = out.str();
t = s.substr(s.find(".")+1);
cout<<"number of decimal places: " << t.length();

What do you mean "stored freely (int"? Once stored in an int, it has zero decimals left, clearly. A double is stored in a binary form, so no obvious or simple relation to "decimals" either. Why don't you keep the input as a string, just long enough to count those decimals, before sending it on to its final numeric-variable destination?

using the Scientific Notation format (to avoid rounding errors):
#include <stdio.h>
#include <string.h>
/* Counting the number of decimals
*
* 1. Use Scientific Notation format
* 2. Convert it to a string
* 3. Tokenize it on the exp sign, discard the base part
* 4. convert the second token back to number
*/
int main(){
int counts;
char *sign;
char str[15];
char *base;
char *exp10;
float real = 0.00001;
sprintf (str, "%E", real);
sign= ( strpbrk ( str, "+"))? "+" : "-";
base = strtok (str, sign);
exp10 = strtok (NULL, sign);
counts=atoi(exp10);
printf("[%d]\n", counts);
return 0;
}
[5]

If the decimal part of your number is stored in a separate int, you can just count the its decimal digits.
This is a improvement on andrei alexandrescu's improvement. His version was already faster than the naive way (dividing by 10 at every digit). The version below is constant time and faster at least on x86-64 and ARM for all sizes, but occupies twice as much binary code, so it is not as cache-friendly.
Benchmarks for this version vs alexandrescu's version on my PR on facebook folly.
Works on unsigned, not signed.
inline uint32_t digits10(uint64_t v) {
return 1
+ (std::uint32_t)(v>=10)
+ (std::uint32_t)(v>=100)
+ (std::uint32_t)(v>=1000)
+ (std::uint32_t)(v>=10000)
+ (std::uint32_t)(v>=100000)
+ (std::uint32_t)(v>=1000000)
+ (std::uint32_t)(v>=10000000)
+ (std::uint32_t)(v>=100000000)
+ (std::uint32_t)(v>=1000000000)
+ (std::uint32_t)(v>=10000000000ull)
+ (std::uint32_t)(v>=100000000000ull)
+ (std::uint32_t)(v>=1000000000000ull)
+ (std::uint32_t)(v>=10000000000000ull)
+ (std::uint32_t)(v>=100000000000000ull)
+ (std::uint32_t)(v>=1000000000000000ull)
+ (std::uint32_t)(v>=10000000000000000ull)
+ (std::uint32_t)(v>=100000000000000000ull)
+ (std::uint32_t)(v>=1000000000000000000ull)
+ (std::uint32_t)(v>=10000000000000000000ull);
}

Years after the fight but as I have made my own solution in three lines :
string number = "543.014";
size_t dotFound;
stoi(number, &dotFound));
string(number).substr(dotFound).size()
Of course you have to test before if it is really a float
(With stof(number) == stoi(number) for example)

int main()
{
char s[100];
fgets(s,100,stdin);
unsigned i=0,sw=0,k=0,l=0,ok=0;
unsigned length=strlen(s);
for(i=0;i<length;i++)
{
if(isprint(s[i]))
{
if(sw==1)
{
k++;
if(s[i]=='0')
{
ok=0;
}
if(ok==0)
{
if(s[i]=='0')
l++;
else
{
ok=1;
l=0;
}
}
}
if(s[i]=='.')
{
sw=1;
}
}
}
printf("%d",k-l);
return 0;
}

This is a robust C++ 11 implementation suitable for float and double types:
template <typename T>
std::enable_if_t<(std::is_floating_point<T>::value), std::size_t>
decimal_places(T v)
{
std::size_t count = 0;
v = std::abs(v);
auto c = v - std::floor(v);
T factor = 10;
T eps = std::numeric_limits<T>::epsilon() * c;
while ((c > eps && c < (1 - eps)) && count < std::numeric_limits<T>::max_digits10)
{
c = v * factor;
c = c - std::floor(c);
factor *= 10;
eps = std::numeric_limits<T>::epsilon() * v * factor;
count++;
}
return count;
}
It throws the value away each iteration and instead keeps track of a power of 10 multiplier to avoid rounding issues building up. It uses machine epsilon to correctly handle decimal numbers that cannot be represented exactly in binary such as the value of 5.2155 as stipulated in the question.

Based on what others wrote, this has worked well for me. This solution does handle the case where a number can't be represented exactly in binary.
As suggested by others, the condition for the while loop indicates the precise behavior. My update uses the machine epsilon value to test whether the remainder on any loop is representable by the numeric format. The test should not compare to 0 or a hardcoded value like 0.000001.
template<class T, std::enable_if_t<std::is_floating_point_v<T>, T>* = nullptr>
unsigned int NumDecimalPlaces(T val)
{
unsigned int decimalPlaces = 0;
val = std::abs(val);
val = val - std::round(val);
while (
val - std::numeric_limits<T>::epsilon() > std::numeric_limits<T>::epsilon() &&
decimalPlaces <= std::numeric_limits<T>::digits10)
{
std::cout << val << ", ";
val = val * 10;
++decimalPlaces;
val = val - std::round(val);
}
return val;
}
As an example, if the input value is 2.1, the correct solution is 1. However, some other answers posted here would output 16 if using double precision because 2.1 can't be precisely represented in double precision.

I would suggest reading the value as a string, searching for the decimal point, and parsing the text before and after it as integers. No floating point or rounding errors.

char* fractpart(double f)
{
int intary={1,2,3,4,5,6,7,8,9,0};
char charary={'1','2','3','4','5','6','7','8','9','0'};
int count=0,x,y;
f=f-(int)f;
while(f<=1)
{
f=f*10;
for(y=0;y<10;y++)
{
if((int)f==intary[y])
{
chrstr[count]=charary[y];
break;
}
}
f=f-(int)f;
if(f<=0.01 || count==4)
break;
if(f<0)
f=-f;
count++;
}
return(chrstr);
}

Here is the complete program
#include <iostream.h>
#include <conio.h>
#include <string.h>
#include <math.h>
char charary[10]={'1','2','3','4','5','6','7','8','9','0'};
int intary[10]={1,2,3,4,5,6,7,8,9,0};
char* intpart(double);
char* fractpart(double);
int main()
{
clrscr();
int count = 0;
double d = 0;
char intstr[10], fractstr[10];
cout<<"Enter a number";
cin>>d;
strcpy(intstr,intpart(d));
strcpy(fractstr,fractpart(d));
cout<<intstr<<'.'<<fractstr;
getche();
return(0);
}
char* intpart(double f)
{
char retstr[10];
int x,y,z,count1=0;
x=(int)f;
while(x>=1)
{
z=x%10;
for(y=0;y<10;y++)
{
if(z==intary[y])
{
chrstr[count1]=charary[y];
break;
}
}
x=x/10;
count1++;
}
for(x=0,y=strlen(chrstr)-1;y>=0;y--,x++)
retstr[x]=chrstr[y];
retstr[x]='\0';
return(retstr);
}
char* fractpart(double f)
{
int count=0,x,y;
f=f-(int)f;
while(f<=1)
{
f=f*10;
for(y=0;y<10;y++)
{
if((int)f==intary[y])
{
chrstr[count]=charary[y];
break;
}
}
f=f-(int)f;
if(f<=0.01 || count==4)
break;
if(f<0)
f=-f;
count++;
}
return(chrstr);
}

One way would be to read the number in as a string. Find the length of the substring after the decimal point and that's how many decimals the person entered. To convert this string into a float by using
atof(string.c_str());
On a different note; it's always a good idea when dealing with floating point operations to store them in a special object which has finite precision. For example, you could store the float points in a special type of object called "Decimal" where the whole number part and the decimal part of the number are both ints. This way you have a finite precision. The downside to this is that you have to write out methods for arithmetic operations (+, -, *, /, etc.), but you can easily overwrite operators in C++. I know this deviates from your original question, but it's always better to store your decimals in a finite form. In this way you can also answer your question of how many decimals the number has.

Related

Is there a way to convert a base 2^64 number to its base10 value in string form or display it in standard out in C or C++ without using big num libs?

Let's say I have a very large number represented using an array of unsigned long(int64), and I want to see its base10 form either stored in a string and/or display it to the standard out directly, how would I do that in C or C++ without using libraries like gmp or boost?, what algorithm or method should I know?
below is an example base2^64 number, with its base10 value in the comment
// base2^64
unsigned long big_num[3] = [77478, 656713, 872];
// base10 = 26364397224300470284329554475476558257587048
I don't exactly know if this is the correct way to convert another number base to base 10, but this is what I did:
To get the base10 value 26364397224300470284329554475476558257587048, I summed up all the digits of the base2^64 number that is multiplied to its base and raised by the index of the digit.
base10 = ((77478 * ((2^64)^2)) + ((656713 * ((2^64)^1))) + ((872 * ((2^64)^0))))
= 26364397224300470284329554475476558257587048
the only problem with this is that there is no primitive data type that can hold this super large sum...
I was just thinking if libraries like boost cpp_int and gmp represents their number like this, and if yes how do they convert it to it's base10 value in string form or display the base10 value in standard out?
Or do they just use half of the bits of the data types that they use like for example in unsigned long and maybe use something like base 10000?
Repeatedly "mod 10" the array to find the next least significant decimal digit, then "divide by 10". Repeat as needed.
Avoid unsigned long to encode 64-bit values as it may be only 32-bit.
If code can encode the number not using the widest type and use uin32_t, then doing the repeated "mod 10" of the array is not so hard.
Below illustrative code still needs to reverse the string - something left for OP. Potential other warts too - hence the advantage of using big number libraries for this sort of thing.
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
// Form reverse decimal string
void convert(char dec[], size_t n, uint32_t b32[]) {
// TBD code to handle 0
while (n > 0 && b32[0] == 0) {
b32++;
n--;
}
while (n > 0) {
unsigned char rem = 0;
// Divide by 10.
for (size_t i = 0; i < n; i++) {
uint64_t sum = rem * (1ULL << 32) + b32[i];
b32[i] = (uint32_t) (sum / 10u);
rem = (unsigned char) (sum % 10u);
}
*dec++ = (char) (rem + '0');
if (b32[0] == 0) {
b32++;
n--;
}
}
*dec = 0;
}
Sample
int main() {
// unsigned long big_num[3] = [77478, 656713, 872];
uint32_t big_num[6] = {0, 77478, 0, 656713, 0, 872};
size_t n = sizeof big_num / sizeof big_num[0];
char s[sizeof big_num * 10 + 1];
convert(s, n, big_num);
printf("<%s>\n", s);
// <84078575285567457445592348207400342279346362>
// 26364397224300470284329554475476558257587048
}
To get the decimal representation of this number, you need to repeatedly divide the number by 10 and take the remainder to get the decimal digits. This means you need to implement long division for big numbers, which also requires implementing long addition, subtraction, and multiplication.
That's a lot of code that big number libraries give you, so just use one.

Get printf to ignore the negative sign on values of zero

I'm trying to write a (mostly)* C program that sorts numerical results and eliminates duplicates. The results are stored as STRUCTS that contain a string, an integer, and 4 doubles. The doubles are what is relevant for determining if two results are duplicates.
To do this, I sprintf a string using the 4 doubles to some precision i.e.
#define PRECISION 5
sprintf(hashString, "%.*lf %.*lf %.*lf %.*lf", PRECISION, result.v1, PRECISION, result.v2, PRECISION, result.v3, PRECISION, result.v4);
I then use this as a hashkey for a tr1::unordered_map<string, ResultType>. Then the program checks to see if the hashtable already contains an entry for that key, if so, the result is a duplicate and can be discarded. Otherwise, it gets added to the hashtable.
The problem is that sometimes one of my values will be rounded to zero from, for example, -10E-9, by sprintf; As a result, the string will contain "-0.00000" rather than "0.00000". These two values will obviously generate different hashkeys, despite representing the same result.
Is there something built into sprintf or even the C language that will allow me to deal with this? I've come up with a bit of a work around (see post below) -- but if there's something built in, I would much rather use that.
*the program is written in C because that's the language I'm most comfortable in, but I'll end up compiling with g++ in order to use the unordered_map.
I've come up with the following workaround. But A) I'm hoping there's a builtin solution and B) I don't have a very deep understanding of atof or floating point math, so I'm not sure if the condition if(doubleRepresentation == 0.0) will always trip when it should.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define PRECISION 5
#define ACCURACY 10E-6
double getRidOfNegZeros (double number)
{
char someNumAsStr[PRECISION + 3]; // +3 accounts for a possible minus sign, the leading 0 or 1, and the decimal place.
sprintf(someNumAsStr, "%.*lf", PRECISION, number);
double doubleRepresentation = atof(someNumAsStr);
if((doubleRepresentation < ACCURACY) && (doubleRepresentation > -ACCURACY))
{
doubleRepresentation = 0.0;
}
return doubleRepresentation;
}
int main()
{
printf("Enter a number: \n");
double somenum;
scanf("%lf",&somenum);
printf("The new representation of double \"%.*lf\" is \"%.*lf\"\n", PRECISION, somenum, PRECISION, getRidOfNegZeros(somenum));
return 0;
}
Rather than sprintf()ing the doubles to a big string and using that as the key in a map, why not just put your structs into the map? You can do this easily enough if you just write a less-than operator for your structs which considers the floating-point values you want to use as the key. Something like this:
bool operator <(const MyStruct &lhs, const MyStruct &rhs)
{
return lhs.v1 < rhs.v1 ||
(lhs.v1 == rhs.v1 && lhs.v2 < rhs.v2); // ...
}
Then you can replace your tr1::unordered_map<string, ResultType> with std::map<ResultType>, and avoid the whole string printing business all together. If you want you can add some epsilon to the comparison function so that numbers that are nearly the same are stably sorted.
If you know that you only care about differences of 0.00001 (based on your definition of PRECISION), you can round the values to integers first. Something like this may work:
#include <math.h>
#include <stdio.h>
#define SCALE 1e5 // instead of PRECISION 5
sprintf(hashString, "%d %d %d %d",
(int)round(result.v1 * SCALE),
(int)round(result.v2 * SCALE),
(int)round(result.v3 * SCALE),
(int)round(result.v4 * SCALE));
This also requires a bound on the magnitude of the floating-point values. You don't want to overflow your integer values.
You can also bypass the string formatting and simply do the rounding calculations as part of a structure-level hash, as others have suggested.
Perhaps implement a utility function to round/snap values to positive zero. Use precision digit count similar to printf style format syntax.
// Prevent display of -0 values by snapping to positive zero
// \a_number original number
// \a_precisionCount number of digits of decimal precision eg. 2 for #.##, 0 for whole integer. Default 0 (whole integer number.)
// \returns number rounded to positive zero if result would have produced -0.00 for precision.
template <class Real>
Real PosZero(const Real& a_number, const int a_precisionCount = 0)
{
Real precisionValue = Real(0.5) * pow(Real(0.10), Real(a_precisionCount));
if( (a_number > -abs(precisionValue)) && (a_number < abs(precisionValue)) )
{
return +0.0;
}
return a_number;
}
Test:
f32 value = -0.049f;
int precision = 4; // Test precision from param
printf("%.0f, %.2f, %.*f", PosZero(value), PosZero(value,2), precision, PosZero(value,precision));
Test output:
"0, -0.05, -0.0490"
This is intended to be a general solution for people wanting to avoid negative zeros in formatted strings. Not specific to the original poster's use of creating a key or hash.
#include <string>
#define PRECISION 5
#define LIMIT 5e-6
std::string string_rep (double x) {
char buf[32];
double xtrunc = ((x > -LIMIT) && (x < LIMIT)) ? 0.0 : x;
std::sprintf (buf, "%.*f", PRECISION, xtrunc);
return std::string(buf);
}
std::string make_key (double x, double y, double z, double w) {
std::string strx = string_rep (x);
std::string stry = string_rep (y);
std::string strz = string_rep (z);
std::string strw = string_rep (w);
return strx + " " + stry + " " + strz + " " + strw;
}
If you're only using this for the purposes of hashing the double values, then don't bother converting them to a string—just hash the double values directly. Any hash library worth its salt will have the ability to hash arbitrary binary blobs of data.
If for some strange reason your hash library only supports null-terminated C strings, then print out the raw bytes of the double value:
// Alias the double value as a byte array
unsigned char *d = (unsigned char *)&result.v1;
// Prefer snprintf to sprintf!
spnrintf(hashString, hashStringLength, "%02x%02x%02x%02x%02x%02x%02x%02x",
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]);
// ...and so on for each double value
This ensures that unequal values will definitely be given unequal strings.

How to convert string (22.123) format number into float variable format without using any API in c++

How to convert string (22.123) format number into float variable format without using any API in c++. This is just to understand more about the inside coding.. thnx
something like:
double string_to_double(std::string s)
{
int p = 0;
int p_dec = s.length();
double val = 0;
for (int i=0; i<s.length(); ++i)
{
double digit = (double)(s[i] - '0');
if (s[i] == '.') { p_dec = p; }
else { val += digit*powf(10,p--); }
}
val /= powf(10, p_dec);
}
Basic algorithm, assuming no input in the form 1.2e-4:
(1) Read an integer before the dot. If the number of digits is > 16 (normal precision of double), convert that integer into floating point directly and return.
(2) Read an at most 16 digits dot as an integer. Compute (that integer) ÷ 10digits read. Sum up this with the integer in step (1) and return.
This only involve 2 floating point operation: one + and one ÷, and a bunch of integer arithmetics. The advantage over multiplications and divisions by powers of 10 is that the error won't accumulate unnecessarily.
(To read 16-digit integers you need a 64-bit int.)
In reality, you should use sscanf(str, "%lf", ...), std::istringstream, or boost::lexical_cast<double>.
go over the number digit by digit by using a bunch of multiplications and divisions by powers of 10 and construct the string character by character.
If you just want an idea of how to do it, the other answer, if you want an accurate result, the problem is not so simple and you should refer to the literature on the subject. An example: ftp://ftp.ccs.neu.edu/pub/people/will/howtoread.ps
I'm pretty sure that the Plauger Standard C Library book has a disc with the source of strtod.
http://www.amazon.co.uk/Standard-C-Library-P-J-Plauger/dp/0131315099
and there are online versions too:
http://www.google.co.uk/search?hl=en&client=firefox-a&hs=IvI&rls=org.mozilla%3Aen-GB%3Aofficial&q=strtod+source+code

How do I convert double to string using only math.h?

I am trying to convert a double to a string in a native NT application, i.e. an application that only depends on ntdll.dll. Unfortunately, ntdll's version of vsnprintf does not support %f et al., forcing me to implement the conversion on my own.
The aforementioned ntdll.dll exports only a few of the math.h functions (floor, ceil, log, pow, ...). However, I am reasonably sure that I can implement any of the unavailable math.h functions if necessary.
There is an implementation of floating point conversion in GNU's libc, but the code is extremely dense and difficult to comprehent (the GNU indentation style does not help here).
I've already implemented the conversion by normalizing the number (i.e. multiplying/dividing the number by 10 until it's in the interval [1, 10)) and then generating each digit by cutting the integral part off with modf and multiplying the fractional part by 10. This works, but there is a loss of precision (only the first 15 digits are correct). The loss of precision is, of course, inherent to the algorithm.
I'd settle with 17 digits, but an algorithm that would be able to generate an arbitrary number of digits correctly would be preferred.
Could you please suggest an algorithm or point me to a good resource?
Double-precision numbers do not have more than 15 significant (decimal) figures of precision. There is absolutely no way you can get "an arbitrary number of digits correctly"; doubles are not bignums.
Since you say you're happy with 17 significant figures, use long double; on Windows, I think, that will give you 19 significant figures.
I've thought about this a bit more. You lose precision because you normalize by multiplying by some power of 10 (you chose [1,10) rather than [0,1), but that's a minor detail). If you did so with a power of 2, you'd lose no precision, but then you'd get "decimal digits"*2^e; you could implement bcd arithmetic and compute the product yourself, but that doesn't sound like fun.
I'm pretty confident that you could split the double g=m*2^e into two parts: h=floor(g*10^k) and i=modf(g*10^k) for some k, and then separately convert to decimal digits and then stitch them together, but how about a simpler approach: use "long double" (80 bits, but I've heard that Visual C++ may not support it?) with your current approach and stop after 17 digits.
_gcvt should do it (edit - it's not in ntdll.dll, it's in some msvcrt*.dll?)
As for decimal digits of precision, IEEE binary64 has 52 binary digits. 52*log10(2)=15.65... (edit: as you pointed out, to round trip, you need more than 16 digits)
After a lot of research, I found a paper titled Printing Floating-Point Numbers Quickly and Accurately. It uses exact rational arithmetic to avoid precision loss. It cites a little older paper: How to Print Floating-Point Numbers Accurately, which however seems to require ACM subscription to access.
Since the former paper was reprinted in 2006, I am inclined to believe that it is still current. The exact rational arithmetic (which requires dynamic allocation) seems to be a necessary evil.
A complete implementation of the C code for the fastest known (as of today) algorithm:
http://code.google.com/p/double-conversion/downloads/list
It even includes a test suite.
This is the C code behind the algorithm described in this PDF:
Printing Floating-Point Numbers Quickly and Accurately
http://www.cs.indiana.edu/~burger/FP-Printing-PLDI96.pdf
#include <cstdint>
// --------------------------------------------------------------------------
// Return number of decimal-digits of a given unsigned-integer
// N is unit8_t/uint16_t/uint32_t/uint64_t
template <class N> inline uint8_t GetUnsignedDecDigits(const N n)
{
static_assert(std::numeric_limits<N>::is_integer && !std::numeric_limits<N>::is_signed,
"GetUnsignedDecDigits: unsigned integer type expected" );
const uint8_t anMaxDigits[]= {3, 5, 8, 10, 13, 15, 17, 20};
const uint8_t nMaxDigits = anMaxDigits[sizeof(N)-1];
uint8_t nDigits= 1;
N nRoof = 10;
while ((n >= nRoof) && (nDigits<nMaxDigits))
{
nDigits++;
nRoof*= 10;
}
return nDigits;
}
// --------------------------------------------------------------------------
// Convert floating-point value to NULL-terminated string represention
TCHAR* DoubleToStr(double f , // [i ]
TCHAR* pczStr , // [i/o] caller should allocate enough space
int nDigitsI, // [i ] digits of integer part including sign / <1: auto
int nDigitsF ) // [i ] digits of fractional part / <0: auto
{
switch (_fpclass(f))
{
case _FPCLASS_SNAN:
case _FPCLASS_QNAN: _tcscpy_s(pczStr, 5, _T("NaN" )); return pczStr;
case _FPCLASS_NINF: _tcscpy_s(pczStr, 5, _T("-INF")); return pczStr;
case _FPCLASS_PINF: _tcscpy_s(pczStr, 5, _T("+INF")); return pczStr;
}
if (nDigitsI> 18) nDigitsI= 18; if (nDigitsI< 1) nDigitsI= -1;
if (nDigitsF> 18) nDigitsF= 18; if (nDigitsF< 0) nDigitsF= -1;
bool bNeg= (f<0);
if (f<0)
f= -f;
int nE= 0; // exponent (displayed if != 0)
if ( ((-1 == nDigitsI) && (f >= 1e18 )) || // large value: switch to scientific representation
((-1 != nDigitsI) && (f >= pow(10., nDigitsI))) )
{
nE= (int)log10(f);
f/= (double)pow(10., nE);
if (-1 != nDigitsF)
nDigitsF= __max(nDigitsF, nDigitsI+nDigitsF-(bNeg?2:1)-4);
nDigitsI= (bNeg?2:1);
}
else if (f>0)
if ((-1 == nDigitsF) && (f <= 1e-10)) // small value: switch to scientific representation
{
nE= (int)log10(f)-1;
f/= (double)pow(10., nE);
if (-1 != nDigitsF)
nDigitsF= __max(nDigitsF, nDigitsI+nDigitsF-(bNeg?2:1)-4);
nDigitsI= (bNeg?2:1);
}
double fI;
double fF= modf(f, &fI); // fI: integer part, fF: fractional part
if (-1 == nDigitsF) // figure out number of meaningfull digits in fF
{
double fG, fGI, fGF;
do
{
nDigitsF++;
fG = fF*pow(10., nDigitsF);
fGF= modf(fG, &fGI);
}
while (fGF > 1e-10);
}
const double afPower10[20]= {1e0 , 1e1 , 1e2 , 1e3 , 1e4 , 1e5 , 1e6 , 1e7 , 1e8 , 1e9 ,
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19 };
uint64_t uI= (uint64_t)round(fI );
uint64_t uF= (uint64_t)round(fF*afPower10[nDigitsF]);
if (uF)
if (GetUnsignedDecDigits(uF) > nDigitsF) // X.99999 was rounded to X+1
{
uF= 0;
uI++;
if (nE)
{
uI/= 10;
nE++;
}
}
uint8_t nRealDigitsI= GetUnsignedDecDigits(uI);
if (bNeg)
nRealDigitsI++;
int nPads= 0;
if (-1 != nDigitsI)
{
nPads= nDigitsI-nRealDigitsI;
for (int i= nPads-1; i>=0; i--) // leading spaces
pczStr[i]= _T(' ');
}
if (bNeg) // minus sign
{
pczStr[nPads]= _T('-');
nRealDigitsI--;
nPads++;
}
for (int j= nRealDigitsI-1; j>=0; j--) // digits of integer part
{
pczStr[nPads+j]= (uint8_t)(uI%10) + _T('0');
uI /= 10;
}
nPads+= nRealDigitsI;
if (nDigitsF)
{
pczStr[nPads++]= _T('.'); // decimal point
for (int k= nDigitsF-1; k>=0; k--) // digits of fractional part
{
pczStr[nPads+k]= (uint8_t)(uF%10)+ _T('0');
uF /= 10;
}
}
nPads+= nDigitsF;
if (nE)
{
pczStr[nPads++]= _T('e'); // exponent sign
if (nE<0)
{
pczStr[nPads++]= _T('-');
nE= -nE;
}
else
pczStr[nPads++]= _T('+');
for (int l= 2; l>=0; l--) // digits of exponent
{
pczStr[nPads+l]= (uint8_t)(nE%10) + _T('0');
nE /= 10;
}
pczStr[nPads+3]= 0;
}
else
pczStr[nPads]= 0;
return pczStr;
}
Does vsnprintf supports I64?
double x = SOME_VAL; // allowed to be from -1.e18 to 1.e18
bool sign = (SOME_VAL < 0);
if ( sign ) x = -x;
__int64 i = static_cast<__int64>( x );
double xm = x - static_cast<double>( i );
__int64 w = static_cast<__int64>( xm*pow(10.0, DIGITS_VAL) ); // DIGITS_VAL indicates how many digits after the decimal point you want to get
char out[100];
vsnprintf( out, sizeof out, "%s%I64.%I64", (sign?"-":""), i, w );
Another option is to try to find implementation of gcvt.
Have you looked at the uClibc implementation of printf?

How can I write a C++ function returning true if a real number is exactly representable with a double?

How can I write a C++ function returning true if a real number is exactly representable with a double?
bool isRepresentable( const char* realNumber )
{
bool answer = false;
// what goes here?
return answer;
}
Simple tests:
assert( true==isRepresentable( "0.5" ) );
assert( false==isRepresentable( "0.1" ) );
Parse the number into the form a + N / (10^k), where a and N are integers, and k is the number of decimal places you have.
Example: 12.0345 -> 12 + 345 / 10^4, a = 12, N = 345, k = 4
Now, 10^k = (2 * 5) ^ k = 2^k * 5^k
You can represent your number as exact binary fraction if and only if you get rid of the 5^k term in the denominator.
The result would check (N mod 5^k) == 0
Holy homework, batman! :)
What makes this interesting is that you can't simply do an (atof|strtod|sscanf) -> sprintf loop and check whether you got the original string back. sprintf on many platforms detects the "as close as you can get to 0.1" double and prints it as 0.1, for example, even though 0.1 isn't precisely representable.
#include <stdio.h>
int main() {
printf("%llx = %f\n",0.1,0.1);
}
prints:
3fb999999999999a = 0.100000
on my system.
The real answer probably would require parsing out the double to convert it to an exact fractional representation (0.1 = 1/10) and then making sure that the atof conversion times the denominator equals the numerator.
I think.
Here is my version. sprintf converts 0.5 to 0.50000, zeros at the end have to be removed.
EDIT: Has to be rewritten to handle numbers without decimal point that end with 0 correctly (like 12300).
bool isRepresentable( const char* realNumber )
{
bool answer = false;
double dVar = atof(realNumber);
char check[20];
sprintf(check, "%f", dVar);
// Remove zeros at end - TODO: Only do if decimal point in string
for (int i = strlen(check) - 1; i >= 0; i--) {
if (check[i] != '0') break;
check[i] = 0;
}
answer = (strcmp(realNumber, check) == 0);
return answer;
}
This should do the trick:
bool isRepresentable(const char *realNumber)
{
double value = strtod(realNumber, NULL);
char test[20];
sprintf(test, "%f", value);
return strcmp(realNumber, test) == 0;
}
Probably best to use the 'safe' version of sprintf to prevent a potential buffer overrun (is it even possible in this case?)
I'd convert the string to its numeric bit representation, (a bit array or a long), then convert the string to a double and see if they match.
Convert the string into a float with a larger scope than a double. Cast that to a double and see if they match.