for a course at my university i need to write a program which converts a number into
natural language e.g. if the user enters "2.55" the program outputs "two point five five".
And i'm close to finish it the only thing i cant get are numbers after the point.
I'm not allowed to use strings. Here is how i tried to get the count of decimal places:
i=0;
while((wert - (long int)wert) != 0){
wert /= 10;
i++;
}
But it gives me the value of 356 decimal places for the example number "2.55". Is there any method wihout using strings to count the decimal places?
Best Regards
You will not be able to use a double to do this due to floating point precision. For example, a double set to 0.2 will have many more decimal places than 1 since 0.2 cannot be represented precisely. (Why not examine the actual value of the double in your debugger?) 2.55 cannot be represented precisely either.
Probably the simplest thing for you to do here is use a string to represent your number and base your parser on that.
If the user is entering the number, it will be coming in as a string, or shall we say into a byte array, so there is no need to bring floating-point into it at all. Just locate the decimal place in the data and then start counting digits.
NB The question in your title is meaningless. Floating-point variables don't have decimal places, they have binary places, and decimal and binary places are incommensurable. If your professor wants you to use floating-point he needs re-education himself.
The IEEE 64-bit binary number closest to 2.55 is 2.54999999999999982236431605997495353221893310546875, which only has 50 digits after the decimal point.
The repeated division makes no sense. Ignoring rounding error, the values of wert would be 2.55, 0.255, 0.0255, 0.00255, etc. none of which is equal to any long. The loop terminates when wert underflows to zero.
Instead, you should be multiplying by 10. However, if you keep the leading digits you may get a number too large to store as a long before getting equality. Instead, I suggest subtracting off the integer part at each step, and stopping when the result is zero.
That will still lead to a 50 decimal place answer, but if your professor insists on double, maybe that is what is wanted.
char* fractpart(double f)
{
char chrstr[5];
char charary={'1','2','3',....,'0'};
int intary={1,2,3,...,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);
}
You have to know the maximal number of digits you have to support after the point.Let's say n digits maximum. Multiply the fractional part of your number by 10^n and then shift the resulting number right until the last digit is different from 0 to remove trailing zeroes. Then print the digits one by one:
void printDigitsAfterPoint(double x) {
int k = Math.round((x-(int) x)*Math.pow10(n));
// remove trailing zeroes
while (k!=0 && k%10==0) {
k/=10;
}
// output digits
while (k!=0) {
output(k%10); // you already should have a method like this...
k/=10;
}
}
Make sure to add code for handling negative values of x. I'm sure your prof will try that...
EDIT: Forgot to change the declaration. Fixed.
Related
This question already has answers here:
Round a float to a regular grid of predefined points
(11 answers)
Closed 4 years ago.
I am calculating the number of significant numbers past the decimal point. My program discards any numbers that are spaced more than 7 orders of magnitude apart after the decimal point. Expecting some error with doubles, I accounted for very small numbers popping up when subtracting ints from doubles, even when it looked like it should equal zero (To my knowledge this is due to how computers store and compute their numbers). My confusion is why my program does not handle this unexpected number given this random test value.
Having put many cout statements it would seem that it messes up when it tries to cast the final 2. Whenever it casts it casts to 1 instead.
bool flag = true;
long double test = 2029.00012;
int count = 0;
while(flag)
{
test = test - static_cast<int>(test);
if(test <= 0.00001)
{
flag = false;
}
test *= 10;
count++;
}
The solution I found was to cast only once at the beginning, as rounding may produce a negative and terminate prematurely, and to round thenceforth. The interesting thing is that both trunc and floor also had this issue, seemingly turning what should be a 2 into a 1.
My Professor and I were both quite stumped as I fully expected small numbers to appear (most were in the 10^-10 range), but was not expecting that casting, truncing, and flooring would all also fail.
It is important to understand that not all rational numbers are representable in finite precision. Also, it is important to understand that set of numbers which are representable in finite precision in decimal base, is different from the set of numbers that are representable in finite precision in binary base. Finally, it is important to understand that your CPU probably represents floating point numbers in binary.
2029.00012 in particular happens to be a number that is not representable in a double precision IEEE 754 floating point (and it indeed is a double precision literal; you may have intended to use long double instead). It so happens that the closest number that is representable is 2029.000119999999924402800388634204864501953125. So, you're counting the significant digits of that number, not the digits of the literal that you used.
If the intention of 0.00001 was to stop counting digits when the number is close to a whole number, it is not sufficient to check whether the value is less than the threshold, but also whether it is greater than 1 - threshold, as the representation error can go either way:
if(test <= 0.00001 || test >= 1 - 0.00001)
After all, you can multiple 0.99999999999999999999999999 with 10 many times until the result becomes close to zero, even though that number is very close to a whole number.
As multiple people have already commented, that won't work because of limitations of floating-point numbers. You had a somewhat correct intuition when you said that you expected "some error" with doubles, but that is ultimately not enough. Running your specific program on my machine, the closest representable double to 2029.00012 is 2029.0001199999999244 (this is actually a truncated value, but it shows the series of 9's well enough). For that reason, when you multiply by 10, you keep finding new significant digits.
Ultimately, the issue is that you are manipulating a base-2 real number like it's a base-10 number. This is actually quite difficult. The most notorious use cases for this are printing and parsing floating-point numbers, and a lot of sweat and blood went into that. For example, it wasn't that long ago that you could trick the official Java implementation into looping endlessly trying to convert a String to a double.
Your best shot might be to just reuse all that hard work. Print to 7 digits of precision, and subtract the number of trailing zeroes from the result:
#include <iostream>
#include <sstream>
#include <iomanip>
#include <string>
int main() {
long double d = 2029.00012;
auto double_string = (std::stringstream() << std::fixed << std::setprecision(7) << d).str();
auto first_decimal_index = double_string.find('.') + 1;
auto last_nonzero_index = double_string.find_last_not_of('0');
if (last_nonzero_index == std::string::npos) {
std::cout << "7 significant digits\n";
} else if (last_nonzero_index < first_decimal_index) {
std::cout << -(first_decimal_index - last_nonzero_index + 1) << " significant digits\n";
} else {
std::cout << (last_nonzero_index - first_decimal_index) << " significant digits\n";
}
}
It feels unsatisfactory, but:
it correctly prints 5;
the "satisfactory" alternative is possibly significantly harder to implement.
It seems to me that your second-best alternative is to read on floating-point printing algorithms and implement just enough of it to get the length of the value that you're going to print, and that's not exactly an introductory-level task. If you decide to go this route, the current state of the art is the Grisu2 algorithm. Grisu2 has the notable benefit that it will always print the shortest base-10 string that will produce the given floating-point value, which is what you seem to be after.
If you want sane results, you can't just truncate the digits, because sometimes the floating point number will be a hair less than the rounded number. If you want to fix this via a fluke, change your initialization to be
long double test = 2029.00012L;
If you want to fix it for real,
bool flag = true;
long double test = 2029.00012;
int count = 0;
while (flag)
{
test = test - static_cast<int>(test + 0.000005);
if (test <= 0.00001)
{
flag = false;
}
test *= 10;
count++;
}
My apologies for butchering your haphazard indent; I can't abide by them. According to one of my CS professors, "ideally, a computer scientist never has to worry about the underlying hardware." I'd guess your CS professor might have similar thoughts.
I'm doing a little library to be able to do operations with really big numbers, and now I'm trying to implement the decimal part of them.
To generate said numbers, I need a double number to translate it into a char array. The integer part is done, but the decimal part is what is making me struggle.
Take a look at this program:
double a = 123.123456;
double dec = a - int(a);
while (dec) {
dec *= 10;
cout << int(dec); // Here it just outputs the decimal digit for the sake of simplicity.
dec -= int(dec);
}
At first, I thought "Okay, this will do it", but the output, instead of being 123456 it is 123456000000004451067070476710796356201171875.
I'm almost sure that this is because of the precision of the double numbers, but I really want to get the 123456 part.
This problem is even more difficult when I add more decimals to the first number, because the "real" digits intersect with the "non-real" ones.
What should I do?
EDIT:
When the double number has a decimal part of 0 (like 123. or 123.0 or 123.00000000), there is no garbage digits.
I am writing a function in c++ that is supposed to find the largest single digit in the number passed (inputValue). For example, the answer for .345 is 5. However, after a while, the program is changing the inputValue to something along the lines of .3449 (and the largest digit is then set to 9). I have no idea why this is happening. Any help to resolve this problem would be greatly appreciated.
This is the function in my .hpp file
void LargeInput(const double inputValue)
//Function to find the largest value of the input
{
int tempMax = 0,//Value that the temporary max number is in loop
digit = 0,//Value of numbers after the decimal place
test = 0,
powerOten = 10;//Number multiplied by so that the next digit can be checked
double number = inputValue;//A variable that can be changed in the function
cout << "The number is still " << number << endl;
for (int k = 1; k <= 6; k++)
{
test = (number*powerOten);
cout << "test: " << test << endl;
digit = test % 10;
cout << (static_cast<int>(number*powerOten)) << endl;
if (tempMax < digit)
tempMax = digit;
powerOten *= 10;
}
return;
}
You cannot represent real numbers (doubles) precisely in a computer - they need to be approximated. If you change your function to work on longs or ints there won't be any inaccuracies. That seems natural enough for the context of your question, you're just looking at the digits and not the number, so .345 can be 345 and get the same result.
Try this:
int get_largest_digit(int n) {
int largest = 0;
while (n > 0) {
int x = n % 10;
if (x > largest) largest = x;
n /= 10;
}
return largest;
}
This is because the fractional component of real numbers is in the form of 1/2^n. As a result you can get values very close to what you want but you can never achieve exact values like 1/3.
It's common to instead use integers and have a conversion (like 1000 = 1) so if you had the number 1333 you would do printf("%d.%d", 1333/1000, 1333 % 1000) to print out 1.333.
By the way the first sentence is a simplification of how floating point numbers are actually represented. For more information check out; http://en.wikipedia.org/wiki/Floating_point#Representable_numbers.2C_conversion_and_rounding
This is how floating point number work, unfortunately. The core of the problem is that there are an infinite number of floating point numbers. More specifically, there are an infinite number of values between 0.1 and 0.2 and there are an infinite number of values between 0.01 and 0.02. Computers, however, have a finite number of bits to represent a floating point number (64 bits for a double precision number). Therefore, most floating point numbers have to be approximated. After any floating point operation, the processor has to round the result to a value it can represent in 64 bits.
Another property of floating point numbers is that as number get bigger they get less and less precise. This is because the same 64 bits have to be able to represent very big numbers (1,000,000,000) and very small numbers (0.000,000,000,001). Therefore, the rounding error gets larger when working with bigger numbers.
The other issue here is that you are converting from floating point to integer. This introduces even more rounding error. It appears that when (0.345 * 10000) is converted to an integer, the result is closer to 3449 than 3450.
I suggest you don't convert your numbers to integers. Write your program in terms of floating point numbers. You can't use the modulus (%) operator on floating point numbers to get a value for digit. Instead use the fmod function in the C math library (cmath.h).
As other answers have indicated, binary floating-point is incapable of representing most decimal numbers exactly. Therefore, you must reconsider your problem statement. Some alternatives are:
The number is passed as a double (specifically, a 64-bit IEEE-754 binary floating-point value), and you wish to find the largest digit in the decimal representation of the exact value passed. In this case, the solution suggested by user millimoose will work (provided the asprintf or snprintf function used is of good quality, so that it does not incur rounding errors that prevent it from producing correctly rounded output).
The number is passed as a double but is intended to represent a number that is exactly representable as a decimal numeral with a known number of digits. In this case, the solution suggested by user millimoose again works, with the format specification altered to convert the double to decimal with the desired number of digits (e.g., instead of “%.64f”, you could use “%.6f”).
The function is changed to pass the number in another way, such as with decimal floating-point, as a scaled integer, or as a string containing a decimal numeral.
Once you have clarified the problem statement, it may be interesting to consider how to solve it with floating-point arithmetic, rather than calling library functions for formatted output. This is likely to have pedagogical value (and incidentally might produce a solution that is computationally more efficient than calling a library function).
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Why does Visual Studio 2008 tell me .9 - .8999999999999995 = 0.00000000000000055511151231257827?
c++
Hey so i'm making a function to return the number of a digits in a number data type given, but i'm having some trouble with doubles.
I figure out how many digits are in it by multiplying it by like 10 billion and then taking away digits 1 by 1 until the double ends up being 0. however when putting in a double of value say .7904 i never exit the function as it keeps taking away digits which never end up being 0 as the resut of .7904 ends up being 7,903,999,988 and not 7,904,000,000.
How can i solve this problem?? Thanks =) ! oh and any other feed back on my code is WELCOME!
here's the code of my function:
/////////////////////// Numb_Digits() ////////////////////////////////////////////////////
enum{DECIMALS = 10, WHOLE_NUMBS = 20, ALL = 30};
template<typename T>
unsigned long int Numb_Digits(T numb, int scope)
{
unsigned long int length= 0;
switch(scope){
case DECIMALS: numb-= (int)numb; numb*=10000000000; // 10 bil (10 zeros)
for(; numb != 0; length++)
numb-=((int)(numb/pow((double)10, (double)(9-length))))* pow((double)10, (double)(9-length)); break;
case WHOLE_NUMBS: numb= (int)numb; numb*=10000000000;
for(; numb != 0; length++)
numb-=((int)(numb/pow((double)10, (double)(9-length))))* pow((double)10, (double)(9-length)); break;
case ALL: numb = numb; numb*=10000000000;
for(; numb != 0; length++)
numb-=((int)(numb/pow((double)10, (double)(9-length))))* pow((double)10, (double)(9-length)); break;
default: break;}
return length;
};
int main()
{
double test = 345.6457;
cout << Numb_Digits(test, ALL) << endl;
cout << Numb_Digits(test, DECIMALS) << endl;
cout << Numb_Digits(test, WHOLE_NUMBS) << endl;
return 0;
}
It's because of their binary representation, which is discussed in depth here:
http://en.wikipedia.org/wiki/IEEE_754-2008
Basically, when a number can't be represented as is, an approximation is used instead.
To compare floats for equality, check if their difference is lesser than an arbitrary precision.
The easy summary about floating point arithmetic :
http://floating-point-gui.de/
Read this and you'll see the light.
If you're more on the math side, Goldberg paper is always nice :
http://cr.yp.to/2005-590/goldberg.pdf
Long story short : real numbers are stored with a fixed, irregular precision, leading to non obvious behaviors. This is unrelated to the language but more a design choice of how to handle real numbers as a whole.
This is because C++ (like most other languages) can not store floating point numbers with infinte precision.
Floating points are stored like this:
sign * coefficient * 10^exponent if you're using base 10.
The problem is that both the coefficient and exponent are stored as finite integers.
This is a common problem with storing floating point in computer programs, you usually get a tiny rounding error.
The most common way of dealing with this is:
Store the number as a fraction (x/y)
Use a delta that allows small deviations (if abs(x-y) < delta)
Use a third party library such as GMP that can store floating point with perfect precision.
Regarding your question about counting decimals.
There is no way of dealing with this if you get a double as input. You cannot be sure that the user actually sent 1.819999999645634565360 and not 1.82.
Either you have to change your input or change the way your function works.
More info on floating point can be found here: http://en.wikipedia.org/wiki/Floating_point
This is because of the way the IEEE floating point standard is implemented, which will vary depending on operations. It is an approximation of precision. Never use logic of if(float == float), ever!
Float numbers are represented in the form Significant digits × baseexponent(IEEE 754). In your case, float 1.82 = 1 + 0.5 + 0.25 + 0.0625 + ...
Since only a limited digits could be stored, therefore there will be a round error if the float number cannot be represented as a terminating expansion in the relevant base (base 2 in the case).
You should always check relative differences with floating point numbers, not absolute values.
You need to read this, too.
Computers don't store floating point numbers exactly. To accomplish what you are doing, you could store the original input as a string, and count the number of characters.
Hey so I'm making a function that returns the number of decimals, whole numbers, or TOTAL numbers there are, but have been unable to make it work with either of these ways:
multiplying by a really large number like 10 billion doesn't work because of the innacurate way computers store decimals, making 2.3 2.2999575697
Using a StringStream to convert the number to a string and count the characters doesn't work because it requires you to set the stream to a precision which either takes away or adds unnecesary '0' characters if not set to the actual number of decimals
So WHAT DO I DO NOW? somebody please help =( Thanks!
if you wanna see my function that converts the numb to a string here it is:
////////////////////// Numbs_Digits ////////////////////////////////////////////////
template<typename T>
int Numbs_Digits(T numb, int scope)
{
stringstream ss(stringstream::in| stringstream::out), ss2(stringstream::in| stringstream::out);
unsigned long int length= 0;
unsigned long int numb_wholes;
ss2 << (int)numb;
numb_wholes = ss2.str().length(); ss2.flush();
bool all= false;
ss.precision(11); // HOW DO I MAKE THE PRECISION NUMBER THE NUMBER OF DECIMALS?
switch(scope){
case ALL: all = true;
case DECIMALS: ss << fixed << numb;
length += ss.str().length()- (numb_wholes +1); // +1 for the "."
if(all!= true) break;
case WHOLE_NUMBS:
length += numb_wholes;
if(all!= true) break;
default: break;}
return length;};
If you want to know the maximum number of decimal digits that a long double can store, this value is available in the constant LDBL_DIG defined in cfloat. Note that this number is actually an approximation as the values are stored in binary internally, and thus the range of values is not a power of 10.
Only some decimal numbers can be stored in exact form as a floating point number. Because of this there is no way to determine how many decimal places are significant for any decimal number for which this is not true. As hammar suggested, read up on the floating point storage format, I believe that every programmer should have some knowledge of low level stuff like this :D
multiplying by a really large number like 10 billion doesn't work because of the innacurate way computers store decimals, making 2.3 2.2999575697
This is exactly the problem. Would you be able to look at 2.999575697 and tell me it has two decimal places? This number is an example of a decimal number that cannot be stored in exact form using the floating point format. The best you could do is count the significant decimal places stored in the floating point number that best approximates the original decimal number it was given - which I can't imagine would be much use.
Edited for a more accurate explanation.
Can you not set the ios_base precision to the maximum number of decimal digits in the significand on your platform in cfloat.h, and then, using ios_base::setf(), change the floating point formatting to scientific, which will remove any trailing zeroes from the floating point number (you'll just have to trim the exponent off the end)?