I am creating unit tests for a function that rounds "rational" numbers stored as strings. The current rounding implementation casts the strings to a floating point type:
#include <boost/lexical_cast.hpp>
#include <iomanip>
#include <limits>
#include <sstream>
template<typename T = double,
size_t PRECISION = std::numeric_limits<T>::digits10>
std::string Round(const std::string& number)
{
std::stringstream ss{};
ss << std::fixed << std::setprecision(PRECISION);
ss << boost::lexical_cast<T>(number);
return ss.str();
}
In one of my tests, I input the number 3.55, which is represented as 3.5499999... on my machine. It all goes well when rounding from 2 decimals to 10. However, when I round to the first decimal, I unsurprisingly get 3.5 instead of 3.6.
What would be a simple method to avoid this error?
Currently, the best solution I was able to find was to use a multiple precision type:
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <iomanip>
#include <sstream>
template<size_t PRECISION = 10>
std::string Round(const std::string& number)
{
using FixedPrecision =
boost::multiprecision::number<
boost::multiprecision::cpp_dec_float<PRECISION>>;
std::stringstream ss{};
ss << std::fixed << std::setprecision(PRECISION);
ss << FixedPrecision{number};
return ss.str();
}
While this solution addresses the problem in a straightforward way (vs manually parsing strings or creating a Rational number class), I find it overkill for such a simple problem.
To find ways to address this problem, I peeked at some calculators' implementations. I looked at gnome-calculator's source code and found that it uses GNU MPFR. I then looked at SpeedCrunch's implementation and found it re-uses the same code as bc, which employs a rational type (numerator, denominator).
Am I overlooking something?
If you are trying to round strings for a given number of decimal places (n decimal), you can do this directly on the string "the human way" : First check that the string has a decimal point. if it has one, check if it it has an n+1 digit after the decimal point. If it does, but it is less than five, you can substring the head of the string up to the n decimal. If it is greater than five, you have to transform your string, basically backtrack until you find a non '9' digit 'd', replace it with 'd+1' and set all the nines you found to 0. If ALL the digits before the n+1 decimal are nines (say -999.99879) append a 1 at the top(after the sign if there is one), and set all the nines you found to zero (-1000.00879). A bit tedious and somewhat inefficient, but straightforward and follows grammar school intuition.
You're not missing anything. The problem in your first implementation is that it's rounding twice: first in the conversion from string to float, and then a second time in the conversion from float back to string.
Using a multi-precision numeric type like boost's allows you to do the first conversion exactly (without rounding), and that's probably the most elegant way to solve the problem.
If you want to avoid using a multi-precision type, then you have to find some other way to represent a rational number, as was said already in the comments. You can do this with integers, but the result is much longer than the boost solution:
#include <cmath>
#include <cstdlib>
#include <iomanip>
#include <sstream>
std::string Round(const std::string &number, size_t new_places)
{
/* split the string at the decimal point */
auto dot = number.find('.');
if (dot == std::string::npos)
return number;
auto whole_s = number.substr(0, dot);
auto dec_s = number.substr(dot + 1);
/* count the number of decimal places */
auto old_places = dec_s.size();
if(old_places <= new_places)
return number;
/* convert to integer form */
auto whole = atoll(whole_s.c_str());
auto dec = atoll(dec_s.c_str());
auto sign = (whole < 0) ? -1 : 1;
whole = abs(whole);
/* combine into a single integer (123.4567 -> 1234567) */
auto old_denom = (long long)pow(10.0, old_places);
auto numerator = whole * old_denom + dec;
/* remove low digits by division (1234567 -> 12346) */
auto new_denom = (long long)pow(10.0, new_places);
auto scale = old_denom / new_denom;
numerator = (numerator + scale / 2) / scale;
/* split at the decimal point again (12346 -> 123.46) */
whole = sign * (numerator / new_denom);
dec = numerator % new_denom;
/* convert back to string form */
std::ostringstream oss;
oss << whole << '.' << std::setw(new_places) << std::setfill('0') << dec;
return oss.str();
}
i want to do what the title says like this:
int number1;
cin>>number1;
num1len=log10(number1)+1;
cout<<"num of digits is "<<num1len<<"\n";
but when the number of digits is 11 and more the answer is always 7(6+1)
Does anyone knows why or what im i doing wrong?
Floating-point data types, including double, store approximations. What you're finding by calling log10 is the number of places to the left of the decimal point, which is affected by at most one by the approximation process.
The question you asked, how to find the number of decimal digits in a number stored in binary floating-point, is meaningless. The number 7.1 has two decimal digits, however its approximate floating-point representation doesn't use decimal digits at all. To preserve the number of decimal digits, you'd need some decimal representation, not the C++ double data type.
Of course, all of this is applicable only to double, per the question title. Your code snippet doesn't actually use double.
What is 'wrong' is the maximum value which can be stored in a (signed) int :
#include <iostream>
#include <numeric>
int main()
{
std::cout << std::numeric_limits<int>::max() << std::endl;
}
Gives me :
2147483647
You are running past the unsigned 32-bit boundary ... your number of 11 digits or more exceeds 0xFFFFFFFF, and so wraps around.
You need to use either unsigned long long or double for your number1 variable:
#include <iostream>
#include <cstdlib>
#include <cmath>
int
main ( int argc, char * argv[] )
{
unsigned long long num; // or double, but note comments below
std::cin >> num;
std::cout << "Number of digits in " << num << " is " << ( (int) std::log10 ( num ) + 1 ) << std::endl;
return 0;
}
Those large numbers will print in scientific notation by default when you send them to std::cout if you choose to use double as your data type, so you would want to throw some formatting in there. If you use an unsigned long long instead, they will print as they were entered, but you have to be sure that your platform supports unsigned long long.
EDIT: As mentioned by others, use of floating point values has other implications to consider, and is most likely not what you are ultimately trying to achieve. AFAIK, the integral type on a platform that yields the largest positive value is unsigned long long, so depending on the values you are looking to work with, see if that is available to you for use.
Others have pointed out that floating point numbers are approximations, so you can't really get an accurate count of digits in it.
But...you can get something approximate, by writing it out to a std::stringstream object, then converting it to a std::string, and getting the lenght of the said string. You'll of course have to deal with the fact that there may be non-digit characters in the string (like minus sign, decimal point, E for exponent etc). Also the number of digits you obtain in this manner would be dependent on formatting options you choose when writing to the stringstream object. But assuming that you know what formatting options you'd like to use, you can get the number of digits subject to these options.
I was wondering, how long in number of characters would the longest a double printed using fprintf be? My guess is wrong.
Thanks in advance.
Twelve would be a bit of an underestimate. On my machine, the following results in a 317 character long string:
#include <limits>
#include <cstdio>
#include <cstring>
int main()
{
double d = -std::numeric_limits<double>::max();
char str[2048] = "";
std::sprintf(str, "%f", d);
std::size_t length = std::strlen(str);
}
Using %e results in a 14 character long string.
Who knows. The Standard doesn't say how many digits of precision a double provides other than saying it (3.9.1.8) "provides at least as much precision as float," so you don't really know how many characters you'll need to sprintf an arbitrary value. Even if you did know how many digits your implementation provided, there's still the question of exponential formatting, etc.
But there's a MUCH bigger question here. Why the heck would you care? I'm guessing it's because you're trying to write something like this:
double d = ...;
int MAGIC_NUMBER = ...;
char buffer[MAGIC_NUMBER];
sprintf(buffer, "%f", d);
This is a bad way to do this, precisely because you don't know how big MAGIC_NUMBER should be. You can pick something that should be big enough, like 14 or 128k, but then the number you picked is arbitrary, not based on anything but a guess that it will be big enough. Numbers like MAGIC_NUMBER are, not suprisingly, called Magic Numbers. Stay away from them. They will make you cry one day.
Instead, there's a lot of ways to do this string formatting without having to care about buffer sizes, digits of precision, etc, that let you just get on with the buisness of programming. Streams is one:
#include <sstream>
double d = ...;
stringstream ss;
ss << d;
string s = ss.str();
cout << s;
...Boost.Format is another:
#include <boost\format\format.hpp>
double d = ... ;
string s = (boost::format("%1%") % d).str();
cout << s;
Its defined in limits:
std::cout << std::numeric_limits<double>::digits << "\n";
std::cout << std::numeric_limits<double>::digits10 << "\n";
Definition:
digits: number of digits (in radix base) in the mantissa
Equivalent to FLT_MANT_DIG, DBL_MANT_DIG or LDBL_MANT_DIG.
digits10: Number of digits (in decimal base) that can be represented without change.
Equivalent to FLT_DIG, DBL_DIG or LDBL_DIG for floating types.
See: http://www.cplusplus.com/reference/std/limits/numeric_limits/
Of course when you print stuff to a stream you can use the stream manipulators to limit the size of the output.
you can decide it by yourself..
double a=1.1111111111111111111111111111111111111111111111111;
printf("%1.15lf\n", a);
return 0;
./a.out
1.111111111111111
you can print more than 12 characters..
If your machine uses IEEE754 doubles (which is fairly widespread now), then the binary precision is 53 bits; The decimal equivalent is approximately 15.95 (calculated via logarithmic conversion), so you can usually rely on 15 decimal digits of precision.
Consult Double precision floating-point format for a brief discussion.
For a much more in-depth study, the canonical paper is What Every Computer Scientist Should Know About Floating-Point Arithmetic. It gets cited here whenever binary floating point discussions pop up, and is worth a weekend of careful reading.
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.
What is the precise meaning of numeric_limits::digits10?
Some other related questions in stackoverflow made me think it is the maximum precision of a double, but
The following prototype starts working (sucess is true) when precision is greater that 17 ( == 2+numeric_limits::digits10)
With STLPort, readDouble==infinity at the end; with microsoft's STL, readDouble == 0.0.
Has this prototype any kind of meaning :) ?
Here is the prototype:
#include <float.h>
#include <limits>
#include <math.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
int main(int argc, const char* argv[]) {
std::ostringstream os;
//int digit10=std::numeric_limits<double>::digits10; // ==15
//int digit=std::numeric_limits<double>::digits; // ==53
os << std::setprecision(17);
os << DBL_MAX;
std::cout << os.str();
std::stringbuf sb(os.str());
std::istream is(&sb);
double readDouble=0.0;
is >> readDouble;
bool success = fabs(DBL_MAX-readDouble)<0.1;
}
numeric_limits::digits10 is the number of decimal digits that can be held without loss.
For example numeric_limits<unsigned char>::digits10 is 2. This means that an unsigned char can hold 0..99 without loss. If it were 3 it could hold 0..999, but as we all know it can only hold 0..255.
This manual page has an example for floating point numbers, which (when shortened) shows that
cout << numeric_limits<float>::digits10 <<endl;
float f = (float)99999999; // 8 digits
cout.precision ( 10 );
cout << "The float is; " << f << endl;
prints
6
The float is; 100000000
numeric_limits::digits10 specifies the number of decimal digits to the left of the decimal point you can represent without a loss of precision. Each type will have a different number of representable decimal values.
See this very readable paper:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2005.pdf
Although DBL_MAX ( = std::numeric_limits::digits10 = 15 digits) is the minimum guaranteed number of digits for a double, the DBL_MAXDIG10 value (= 17 digits) proposed in the paper has the useful properties:
Of being the minimum number of digits needed to survive a round-trip to string form and back and get the same double in the end.
Of being the minimum number of digits needed to convert the double
to string form and show different strings every time you get (A != B) in code.
With 16 or fewer digits, you can get doubles that are not equal in code,
but when they are converted to string form they are the same
(which will give the case where they are different when compared in the code,
but a log file will show them as identical - very confusing and hard to debug!)
When you compare values (e.g. by reviewing them manually by diff'ing two log files) we should remember that digits 1-15 are ALWAYS valid, but differences in the 16th and 17th digits MAY be junk.
The '53' is the bit width of the significand that your type (double) holds. The '15' is the number of decimal digits that can be represented safely with that kind of precision.
digits10 is for conversion: string → double → string
max_digits10 is for conversion: double → string → double
In your program, you are using the conversion (double → string → double). You should use max_digits10 instead of digits10.
For more details about digits10 and max_digits10, you can read:
difference explained by stackoverflow
digits10
max_digits10