转换为分数英寸
Convert to fraction inches
我需要将双精度值(厘米)转换为分数值,格式为:3 1/64(英寸)。在阅读了很多有关此内容并找到转换为分数的算法之后,我认为它们不适合我的需要,因为我的分数应该采用这些格式:?/2、?/4、?/8、?/16、 ?/32,?/64。我见过这样的转换 tables:table。我认为我最好的解决方案是创建一个键值列表,其中包含 table 中的所有值,并为每个数字在列表中找到最佳近似值。
例如:3.21 厘米。 = 1.26378 英寸 = 1 英寸 + 0.26378。所以,根据 table 链接,0.26378 = 17/64。最后的结果应该是 1 17/64 英寸。
所以我的问题是:
有一个包含 table 中的值的列表并找到最接近的值以给出分数是个好主意,还是为此创建一个算法更好?
如果可以创建包含值的列表,我如何才能在我的列表中找到给定数字的最接近值?
我建议使用简单的数学而不是 table
private static string ToFraction64(double value) {
// denominator is fixed
int denominator = 64;
// integer part, can be signed: 1, 0, -3,...
int integer = (int) value;
// numerator: always unsigned (the sign belongs to the integer part)
// + 0.5 - rounding, nearest one: 37.9 / 64 -> 38 / 64; 38.01 / 64 -> 38 / 64
int numerator = (int) ((Math.Abs(value) - Math.Abs(integer)) * denominator + 0.5);
// some fractions, e.g. 24 / 64 can be simplified:
// both numerator and denominator can be divided by the same number
// since 64 = 2 ** 6 we can try 2 powers only
// 24/64 -> 12/32 -> 6/16 -> 3/8
// In general case (arbitrary denominator) use gcd (Greatest Common Divisor):
// double factor = gcd(denominator, numerator);
// denominator /= factor;
// numerator /= factor;
while ((numerator % 2 == 0) && (denominator % 2 == 0)) {
numerator /= 2;
denominator /= 2;
}
// The longest part is formatting out
// if we have an actual, not degenerated fraction (not, say, 4 0/1)
if (denominator > 1)
if (integer != 0) // all three: integer + numerator + denominator
return string.Format("{0} {1}/{2}", integer, numerator, denominator);
else if (value < 0) // negative numerator/denominator, e.g. -1/4
return string.Format("-{0}/{1}", numerator, denominator);
else // positive numerator/denominator, e.g. 3/8
return string.Format("{0}/{1}", numerator, denominator);
else
return integer.ToString(); // just an integer value, e.g. 0, -3, 12...
}
测试
const double cmInInch = 2.54;
// 1 17/64
Console.Write(ToFraction64(3.21 / cmInInch));
// -1 17/64
Console.Write(ToFraction64(-1.26378));
// 3 1/4
Console.Write(ToFraction64(3.25001));
// 3 1/4
Console.Write(ToFraction64(3.24997));
// 5
Console.Write(ToFraction64(5.000001));
// -1/8
Console.Write(ToFraction64(-0.129));
// 1/8
Console.Write(ToFraction64(0.129));
采用 Dmitry Bychenko 的代码,我们需要添加 case "else if (denominator == numerator)" if fraction equals to 1 如果值为正则加 1 或如果值为负则删除 1 (例如 denominator/numerator = 64/64)
private static string ToFraction64(double value)
{
// denominator is fixed
int denominator = 64;
// integer part, can be signed: 1, 0, -3,...
int integer = (int)value;
// numerator: always unsigned (the sign belongs to the integer part)
// + 0.5 - rounding, nearest one: 37.9 / 64 -> 38 / 64; 38.01 / 64 -> 38 / 64
int numerator = (int)((Math.Abs(value) - Math.Abs(integer)) * denominator + 0.5);
// some fractions, e.g. 24 / 64 can be simplified:
// both numerator and denominator can be divided by the same number
// since 64 = 2 ** 6 we can try 2 powers only
// 24/64 -> 12/32 -> 6/16 -> 3/8
// In general case (arbitrary denominator) use gcd (Greatest Common Divisor):
// double factor = gcd(denominator, numerator);
// denominator /= factor;
// numerator /= factor;
while ((numerator % 2 == 0) && (denominator % 2 == 0))
{
numerator /= 2;
denominator /= 2;
}
// The longest part is formatting out
// if we have an actual, not degenerated fraction (not, say, 4 0/1)
if (denominator > 1)
if (integer != 0) // all three: integer + numerator + denominator
return string.Format("{0} {1}/{2}", integer, numerator, denominator);
else if (value < 0) // negative numerator/denominator, e.g. -1/4
return string.Format("-{0}/{1}", numerator, denominator);
else // positive numerator/denominator, e.g. 3/8
return string.Format("{0}/{1}", numerator, denominator);
//if fraction equals to 1 we add 1 if the value is positive or remove 1 if the value is negative (ex denominator/numerator = 64/64)
else if (denominator == numerator)
{
if (value < 0) // negative numerator/denominator, e.g. -1/4
integer--;
else // positive numerator/denominator, e.g. 3/8
integer++;
return integer.ToString();
}
else
return integer.ToString(); // just an integer value, e.g. 0, -3, 12...
}
我的功能更简单更简单..
它 return 一个包含十进制值的三个整数 {Inches, Numerator, Denominator} 的数组,fracBase 参数表示精度为16、32、64、128 ....
public static int[] GetImpFractions(decimal value, int fracBase = 32)
{
int[] result = { 0, 0, 0 };
result[0] = (int)Math.Truncate(value);
decimal num = (value - (decimal)result[0]);
num *= fracBase;
decimal denom = fracBase;
if (num > 0)
{
while (num % 2 == 0)
{
num /= 2;
denom /= 2;
}
if (num == 1 && denom == 1)
{
result[0] += 1;
num = 0;
denom = 0;
}
result[1] = (int)Math.Truncate(num);
result[2] = (int)Math.Truncate(denom);
}
return result;
}
增加了脚的显示
public static string ToFraction(this double source, int denominator)
{
var divider = denominator;
var inches = (int) Math.Abs(source);
var numerator = (int) ((Math.Abs(source) - Math.Abs(inches)) * divider + 0.5);
while (numerator % 2 == 0 && divider % 2 == 0)
{
numerator /= 2;
divider /= 2;
}
if (divider == numerator)
{
if (source < 0) inches--;
else inches++;
numerator = 0;
}
var feet = Math.DivRem(inches, 12, out inches);
var valueBuilder = new StringBuilder();
if (source + 1d / denominator < 0) valueBuilder.Insert(0, "-");
if (feet > 0)
{
valueBuilder.Append(feet);
valueBuilder.Append("'");
valueBuilder.Append("-");
}
valueBuilder.Append(inches);
if (numerator != 0)
{
valueBuilder.Append(" ");
valueBuilder.Append(numerator);
valueBuilder.Append("/");
valueBuilder.Append(divider);
}
valueBuilder.Append('"');
return valueBuilder.ToString();
}
所有测试都通过了
[TestCase]
public void FractionTest()
{
Assert.AreEqual("0\"", 0d.ToFraction());
Assert.AreEqual("0\"", (-0d).ToFraction());
Assert.AreEqual("0\"", (-0.00001d).ToFraction());
Assert.AreEqual("1\"", 1d.ToFraction());
Assert.AreEqual("-1\"", (-1d).ToFraction());
Assert.AreEqual("0 1/8\"", 0.129.ToFraction());
Assert.AreEqual("-0 1/8\"", (-0.129).ToFraction());
Assert.AreEqual("-1 1/4\"", (-1.26378).ToFraction());
Assert.AreEqual("5\"", 5.000001.ToFraction());
Assert.AreEqual("3 1/4\"", 3.24997.ToFraction());
Assert.AreEqual("3 1/4\"", 3.25001.ToFraction());
Assert.AreEqual("1'-0\"", 12d.ToFraction());
Assert.AreEqual("1'-0 3/32\"", 12.1d.ToFraction());
Assert.AreEqual("1'-1\"", 13d.ToFraction());
Assert.AreEqual("1'-3 1/8\"", 15.125d.ToFraction());
Assert.AreEqual("1'-0\"", 12.00001d.ToFraction());
Assert.AreEqual("-1'-0\"", (-12.00001d).ToFraction());
Assert.AreEqual("-2'-1 7/32\"", (-25.231d).ToFraction());
}
我需要将双精度值(厘米)转换为分数值,格式为:3 1/64(英寸)。在阅读了很多有关此内容并找到转换为分数的算法之后,我认为它们不适合我的需要,因为我的分数应该采用这些格式:?/2、?/4、?/8、?/16、 ?/32,?/64。我见过这样的转换 tables:table。我认为我最好的解决方案是创建一个键值列表,其中包含 table 中的所有值,并为每个数字在列表中找到最佳近似值。
例如:3.21 厘米。 = 1.26378 英寸 = 1 英寸 + 0.26378。所以,根据 table 链接,0.26378 = 17/64。最后的结果应该是 1 17/64 英寸。
所以我的问题是:
有一个包含 table 中的值的列表并找到最接近的值以给出分数是个好主意,还是为此创建一个算法更好?
如果可以创建包含值的列表,我如何才能在我的列表中找到给定数字的最接近值?
我建议使用简单的数学而不是 table
private static string ToFraction64(double value) {
// denominator is fixed
int denominator = 64;
// integer part, can be signed: 1, 0, -3,...
int integer = (int) value;
// numerator: always unsigned (the sign belongs to the integer part)
// + 0.5 - rounding, nearest one: 37.9 / 64 -> 38 / 64; 38.01 / 64 -> 38 / 64
int numerator = (int) ((Math.Abs(value) - Math.Abs(integer)) * denominator + 0.5);
// some fractions, e.g. 24 / 64 can be simplified:
// both numerator and denominator can be divided by the same number
// since 64 = 2 ** 6 we can try 2 powers only
// 24/64 -> 12/32 -> 6/16 -> 3/8
// In general case (arbitrary denominator) use gcd (Greatest Common Divisor):
// double factor = gcd(denominator, numerator);
// denominator /= factor;
// numerator /= factor;
while ((numerator % 2 == 0) && (denominator % 2 == 0)) {
numerator /= 2;
denominator /= 2;
}
// The longest part is formatting out
// if we have an actual, not degenerated fraction (not, say, 4 0/1)
if (denominator > 1)
if (integer != 0) // all three: integer + numerator + denominator
return string.Format("{0} {1}/{2}", integer, numerator, denominator);
else if (value < 0) // negative numerator/denominator, e.g. -1/4
return string.Format("-{0}/{1}", numerator, denominator);
else // positive numerator/denominator, e.g. 3/8
return string.Format("{0}/{1}", numerator, denominator);
else
return integer.ToString(); // just an integer value, e.g. 0, -3, 12...
}
测试
const double cmInInch = 2.54;
// 1 17/64
Console.Write(ToFraction64(3.21 / cmInInch));
// -1 17/64
Console.Write(ToFraction64(-1.26378));
// 3 1/4
Console.Write(ToFraction64(3.25001));
// 3 1/4
Console.Write(ToFraction64(3.24997));
// 5
Console.Write(ToFraction64(5.000001));
// -1/8
Console.Write(ToFraction64(-0.129));
// 1/8
Console.Write(ToFraction64(0.129));
采用 Dmitry Bychenko 的代码,我们需要添加 case "else if (denominator == numerator)" if fraction equals to 1 如果值为正则加 1 或如果值为负则删除 1 (例如 denominator/numerator = 64/64)
private static string ToFraction64(double value)
{
// denominator is fixed
int denominator = 64;
// integer part, can be signed: 1, 0, -3,...
int integer = (int)value;
// numerator: always unsigned (the sign belongs to the integer part)
// + 0.5 - rounding, nearest one: 37.9 / 64 -> 38 / 64; 38.01 / 64 -> 38 / 64
int numerator = (int)((Math.Abs(value) - Math.Abs(integer)) * denominator + 0.5);
// some fractions, e.g. 24 / 64 can be simplified:
// both numerator and denominator can be divided by the same number
// since 64 = 2 ** 6 we can try 2 powers only
// 24/64 -> 12/32 -> 6/16 -> 3/8
// In general case (arbitrary denominator) use gcd (Greatest Common Divisor):
// double factor = gcd(denominator, numerator);
// denominator /= factor;
// numerator /= factor;
while ((numerator % 2 == 0) && (denominator % 2 == 0))
{
numerator /= 2;
denominator /= 2;
}
// The longest part is formatting out
// if we have an actual, not degenerated fraction (not, say, 4 0/1)
if (denominator > 1)
if (integer != 0) // all three: integer + numerator + denominator
return string.Format("{0} {1}/{2}", integer, numerator, denominator);
else if (value < 0) // negative numerator/denominator, e.g. -1/4
return string.Format("-{0}/{1}", numerator, denominator);
else // positive numerator/denominator, e.g. 3/8
return string.Format("{0}/{1}", numerator, denominator);
//if fraction equals to 1 we add 1 if the value is positive or remove 1 if the value is negative (ex denominator/numerator = 64/64)
else if (denominator == numerator)
{
if (value < 0) // negative numerator/denominator, e.g. -1/4
integer--;
else // positive numerator/denominator, e.g. 3/8
integer++;
return integer.ToString();
}
else
return integer.ToString(); // just an integer value, e.g. 0, -3, 12...
}
我的功能更简单更简单.. 它 return 一个包含十进制值的三个整数 {Inches, Numerator, Denominator} 的数组,fracBase 参数表示精度为16、32、64、128 ....
public static int[] GetImpFractions(decimal value, int fracBase = 32)
{
int[] result = { 0, 0, 0 };
result[0] = (int)Math.Truncate(value);
decimal num = (value - (decimal)result[0]);
num *= fracBase;
decimal denom = fracBase;
if (num > 0)
{
while (num % 2 == 0)
{
num /= 2;
denom /= 2;
}
if (num == 1 && denom == 1)
{
result[0] += 1;
num = 0;
denom = 0;
}
result[1] = (int)Math.Truncate(num);
result[2] = (int)Math.Truncate(denom);
}
return result;
}
增加了脚的显示
public static string ToFraction(this double source, int denominator)
{
var divider = denominator;
var inches = (int) Math.Abs(source);
var numerator = (int) ((Math.Abs(source) - Math.Abs(inches)) * divider + 0.5);
while (numerator % 2 == 0 && divider % 2 == 0)
{
numerator /= 2;
divider /= 2;
}
if (divider == numerator)
{
if (source < 0) inches--;
else inches++;
numerator = 0;
}
var feet = Math.DivRem(inches, 12, out inches);
var valueBuilder = new StringBuilder();
if (source + 1d / denominator < 0) valueBuilder.Insert(0, "-");
if (feet > 0)
{
valueBuilder.Append(feet);
valueBuilder.Append("'");
valueBuilder.Append("-");
}
valueBuilder.Append(inches);
if (numerator != 0)
{
valueBuilder.Append(" ");
valueBuilder.Append(numerator);
valueBuilder.Append("/");
valueBuilder.Append(divider);
}
valueBuilder.Append('"');
return valueBuilder.ToString();
}
所有测试都通过了
[TestCase]
public void FractionTest()
{
Assert.AreEqual("0\"", 0d.ToFraction());
Assert.AreEqual("0\"", (-0d).ToFraction());
Assert.AreEqual("0\"", (-0.00001d).ToFraction());
Assert.AreEqual("1\"", 1d.ToFraction());
Assert.AreEqual("-1\"", (-1d).ToFraction());
Assert.AreEqual("0 1/8\"", 0.129.ToFraction());
Assert.AreEqual("-0 1/8\"", (-0.129).ToFraction());
Assert.AreEqual("-1 1/4\"", (-1.26378).ToFraction());
Assert.AreEqual("5\"", 5.000001.ToFraction());
Assert.AreEqual("3 1/4\"", 3.24997.ToFraction());
Assert.AreEqual("3 1/4\"", 3.25001.ToFraction());
Assert.AreEqual("1'-0\"", 12d.ToFraction());
Assert.AreEqual("1'-0 3/32\"", 12.1d.ToFraction());
Assert.AreEqual("1'-1\"", 13d.ToFraction());
Assert.AreEqual("1'-3 1/8\"", 15.125d.ToFraction());
Assert.AreEqual("1'-0\"", 12.00001d.ToFraction());
Assert.AreEqual("-1'-0\"", (-12.00001d).ToFraction());
Assert.AreEqual("-2'-1 7/32\"", (-25.231d).ToFraction());
}