Introduction
A four line algorithm in MC++ for converting a decimal value to three separate pieces: units, numerator, denominator. Their results are suitable to format a string.
Formula
The basic formula is to divide the right side of the decimal value by the decimal equivalent of the fractional measure and round to an integer. This becomes the numerator over the desired denominator (of the conversion fraction). Thus, for converting to an eighth, as 1/8 is .125, one divides the IEEERmainder()
of the decimal by .125 to obtain a numerator over 8.
Convert 3.6742 to 16th: .6742/.0625 = 10.7872. 10.7872 rounds to 11 creating the fraction 11/16. The result is three and eleven sixteen (3 11/16).
Convert 3.6742 to 8th: .6742/.125 = 5.3936 = 5/8. The result is three and five eighth. (3 5/8).
I use Math::IEERemainder(decimal, 1.0)
to separate the decimal from the single
value, and Math::floor(decimal)
to separate the units. If the decimal part is greater than .5 then the Math
algorithm returns a negative complement, and must be subtracted from one. Thus the line:
if (Math::Sign(remainder) == -1) remainder = 1 + remainder
.6742 returns -0.32579942, which, when added to 1.0 results in .6742008.
Due to this check of the sign the algorithm only works for positive numbers.
The Algorithm:
(The decimal and denominator are input)
Single remainder = Math::IEEERemainder(decimal, 1.0);
if (Math::Sign(remainder) == -1) remainder = 1 + remainder;
Int32 units = Convert::ToInt32(Math::Floor(decimal));
Int32 numerator = Convert::ToInt32(Math::Round(remainder/denominator));
Other considerations;
For flexibility, one would prefer to provide an integer specifying the desired conversion rather than hard code, say, .125 as the denominator. Thus input an integer numerator and compute the divisor.
Single divisor = Convert::ToSingle(1)/Convert::ToSingle(denominator);
Int32 numerator = Convert::ToInt32(Math::Round(remainder/divisor));
The algorithm only works for positive decimals, thus one needs to test for flag, correct and restore negativity. Further, problems that need to be considered are the rounding down to zero and rounding up to the next unit, and reduction of the fraction. The following code accounts for these.
The following code was written for a very specific purpose: to convert English inch measurement fractions, specifically, the common fractions 1/8, 1/4 and 1/2. (Although I tested to 1/32.) I was not interested in fractions like 1/5 or 1/7 or 1/324 whatever. The algorithm may be useful to help those, but not the example function. The code is not generalized. But the algorithm is. The code is only provided as a wrapper example.
Code:
#pragma warning( disable : 4244 ) // possible loss of data due to conversion
String* Utils::Form1::SingleToStringFraction(Single decimal, Int32 denominator)
{
bool isneg = (Math::Sign(decimal) == -1) ? true : false;
if (isneg) decimal *= -1;
Single remainder = Math::IEEERemainder(decimal, 1.0);
if (Math::Sign(remainder) == -1) remainder = 1 + remainder;
Int32 units = Convert::ToInt32(Math::Floor(decimal));
Single divisor = Convert::ToSingle(1)/Convert::ToSingle(denominator);
Int32 numerator = Convert::ToInt32(Math::Round(remainder/divisor));
String* fraction;
if ((numerator > 0) && (numerator == denominator))
{
units++;
fraction = S"";
}
else if (numerator == 0)
{
fraction = S"";
}
else
{
while (numerator%2 == 0)
{
numerator /= 2;
denominator /= 2;
}
fraction = String::Format(" {0}/{1}",
numerator.ToString(), denominator.ToString());
}
if (isneg) units *= -1;
#ifdef _DEBUG_CUT
String* rtnstr;
if (isneg) decimal *= -1;
rtnstr = String::Format("{0}{1}", units.ToString(), fraction);
Diagnostics::Trace::WriteLine(rtnstr, decimal.ToString());
#endif
return String::Format("{0}{1}", units.ToString(), fraction);
}
#pragma warning( default : 4244 )
Caveat
I have never claimed to know everything. And, my MC++ skills may be lacking. If you know of a better and more efficient algorithm, or can improve on the quality of the above code, please comment. In the program that I am working on, I will be going back and forth from fractions to decimals regularly. Efficiency would be nice.
Oh yes, one could simply convert to a string and use split on S"."; but what fun in that? And using Math
to split the Single
qualifies as an algorithm while splitting a string does not.
Retired C programmer and Unix Sys Admin, then VC6 C++ MFC programmer. I moved to VC7 C++ 2003 in Oct of 04, and VC8 C++/CLI early in 06. I resisted C#; but now it is my preferred language. I'm through with upgrading. I'll stay at VC2008 and C#, as I only program for fun anymore.
Update 2019: I have moved back to Unix: Linux Cinnamon Mint. I got aggravated with Windows and VC. I've returned to my roots. Now, my preferred language is Java. I use IntelliJ as my IDE; but write my code with Emacs. I thoroughly enjoy Linux and being back on Unix! I've published a little game on the Kindle Fire and a little apt for Android phones. I hope you still love to code as much as I do when you reach my age... advanced 70's.
Update 2024: I'm now 82, and can still code; but not so well as in the past. Recently wrote a little Android Studio app for my Kindle to keep score while playing Mexican Train. Just for my use.