.net 将十进制值按10的幂进行缩放
什么是缩放系统的最佳方式。当已知位数时,十进制值以10的幂进行缩放 我想到了.net 将十进制值按10的幂进行缩放,.net,decimal,.net,Decimal,什么是缩放系统的最佳方式。当已知位数时,十进制值以10的幂进行缩放 我想到了value*(十进制)Math.Pow(10位),但它有两个缺点: 它引入了浮点数,这使得随着数字越来越大,舍入误差很难解释 当您试图做的只是更改十进制数据结构中已编码的简单比例分量时,执行幂运算似乎有些过分 有更好的方法吗?您可以创建一个10次幂的表,如: var pow10s = new int [] { 1, 10, 100, 1000, 10000, 100000, ... }; 然后使用位置作为此表的索引:
value*(十进制)Math.Pow(10位)
,但它有两个缺点:
有更好的方法吗?您可以创建一个10次幂的表,如:
var pow10s = new int [] { 1, 10, 100, 1000, 10000, 100000, ... };
然后使用位置作为此表的索引:
return value * pow10s[place]
更新:
如果您不想在尝试索引数组超过N个位置时崩溃,可以采用稍微复杂一点的方法,如下所示:
public class Power10Scale
{
private static readonly int[] Pow10s = {
1, 10, 100, 1000, 10000, 100000,
};
public static int Up(int value, int places)
{
return Scale(value, places, (x, y) => x * y);
}
public static int Down(int value, int places)
{
return Scale(value, places, (x, y) => x / y);
}
private static int Scale(int value, int places, Func<int, int, int> operation)
{
if (places < Pow10s.Length)
return operation(value, Pow10s[places]);
return Scale(
operation(value, Pow10s[Pow10s.Length - 1]),
places - (Pow10s.Length - 1),
operation);
}
}
public类Power10Scale
{
专用静态只读int[]Pow10s={
1, 10, 100, 1000, 10000, 100000,
};
公共静态整型(整型值,整型位置)
{
返回刻度(值,位置,(x,y)=>x*y);
}
公共静态整型向下(整型值,整型位置)
{
返回刻度(值、位置,(x,y)=>x/y);
}
专用静态整数比例(整数值、整数位置、Func操作)
{
如果(位置<功率长度)
返回操作(值,功率10s[位置]);
回报率(
操作(值,功率10s[功率10s.长度-1]),
位置-(功率10s.长度-1),
操作);
}
}
直接使用小数的小数部分并没有简单的方法。它代表除数指数:你需要从中减去它来获得你想要的行为。值通常为0,但是范围不能低于它-您需要洗牌尾数中的位,这会造成很大伤害
看起来,对于可读性和精度来说,最好的方法是使用十位小数的幂和乘法
另一种方法是采用十进制1e+28,从指数分量中减去刻度,然后乘以刻度数:
public static decimal Scale(decimal number, int places)
{
const byte MaxDivisorExponent = 28;
const int e28_low = 268435456;
const int e28_middle = 1042612833;
const int e28_high = 542101086;
var power = new Decimal(e28_low, e28_middle, e28_high, false, (byte)(MaxDivisorExponent - places));
return number * power;
}
构造函数中的常量是低、中、高整数部分,它们构成十进制1e+28的96位尾数,只需调用即可获得
但我不确定我是否愿意在生产中看到这一点。您可以使用从使用制作的字符串中获得10的幂
需要注意的一个特殊情况是刻度的绝对值超过28
,这应该起作用,例如
Scale(1e-28m, +56) == 1e+28
Scale(1e+28m, -56) == 1e-28
因为调用decimal.Parse
将失败(中间幂不能用decimal
值表示)
代码:
我对下面的代码进行了修改
我甚至可以从.NET源代码中复制和粘贴,只是为了让我感觉更好——或者更糟?:-)请记住不要将9以上的任何内容作为places
:)您可以,我不认为复制表是特别糟糕的。出于兴趣,我可能还会尝试允许overflowSee,为什么需要这种方法?我正在将一个Android应用程序移植到Xamarin.Forms。该应用程序用于与后端对话,并将十进制值编码为缩放的Int32。对于某些值,刻度是可变的,并以位数的形式传输。在Android上,通过直接支持按位置缩放。
/// <summary>
/// Scales value to move the decimal point by a certain number of places
/// </summary>
public static decimal Scale(decimal value, int places)
{
// Handle degenerate case
if ( value == 0 )
return 0;
// Handle the case when the power of ten will overflow.
// Split the problem up into two calls to Scale.
if ( Math.Abs(places) > 28 )
{
var intermediateNumberOfPlaces = places / 2;
var intermediateValue = Scale(value, intermediateNumberOfPlaces);
return Scale(intermediateValue, places - intermediateNumberOfPlaces);
}
// Normal cases
var powerOfTen = getPowerOfTen(Math.Abs(places));
if ( places > 0 )
return value * powerOfTen;
return value / powerOfTen;
}
private static ConcurrentDictionary<int, decimal> powersOfTen = new ConcurrentDictionary<int, decimal>();
private static decimal getPowerOfTen(int power)
{
return powersOfTen.GetOrAdd(power, p =>
{
var powerAsString = "1" + string.Concat(Enumerable.Repeat("0", p));
return decimal.Parse(powerAsString, CultureInfo.InvariantCulture);
});
}
Assert.AreEqual(1, Scale(1, 0), "Invariant scale failed");
Assert.AreEqual(0, Scale(0, 100), "Scale of 0 failed");
Assert.AreEqual(100, Scale(1, 2), "Scale(1, 2) failed");
Assert.AreEqual(0.01, Scale(1, -2), "Scale(1, -2) failed");
Assert.AreEqual(1, Scale(0.01m, 2), "Scale(0.01, 2) failed");
Assert.AreEqual(1, Scale(100, -2), "Scale(100, -2) failed");
var large = Scale(1, 28);
var small = Scale(1, -28);
var shouldBeLarge = Scale(small, 56);
var shouldBeSmall = Scale(large, -56);
Assert.AreEqual(large, shouldBeLarge, "scaling 1e-28 by 56 failed");
Assert.AreEqual(small, shouldBeSmall, "scaling 1e28 by -56 failed");