double值上的string.Format()给出了不准确的结果

时间:2014-04-29 21:59:56

标签: c# math razor double string-formatting

我有这段代码:

<tbody>
    @foreach (var day in user.WorkDays)
    {
        <tr>
            <th>@day.Date.ToString("MM/dd/yy")</th>
            <td>
                <ul>
                    @foreach (var note in day.Notes)
                    {
                        <li>@note.Text</li>
                    }
                </ul>
            </td>
            <td>@string.Format("{0:0.00}", Math.Truncate(day.Totals.Regular * 100) / 100)</td>
            <td>@string.Format("{0:0.00}", Math.Truncate(day.Totals.Overtime * 100) / 100)</td>
            <td>@string.Format("{0:0.00}", Math.Truncate(day.Totals.Doubletime * 100) / 100)</td>
            <td>@string.Format("{0:0.00}", Math.Truncate(day.Totals.Sick * 100) / 100)</td>
            <td>@string.Format("{0:0.00}", Math.Truncate(day.Totals.Vacation * 100) / 100)</td>
            <td>@string.Format("{0:0.00}", Math.Truncate(day.Totals.Holiday * 100) / 100)</td>
            <td>@string.Format("{0:0.00}", Math.Truncate(day.Totals.Overall * 100) / 100)</td>
        </tr>
    }
</tbody>
<tfoot>
    <tr>
        <th>Totals:</th>
        <th></th>
        <th>@string.Format("{0:0.00}", Math.Truncate(user.Totals.Regular * 100) / 100)</th>
        <th>@string.Format("{0:0.00}", Math.Truncate(user.Totals.Overtime * 100) / 100)</th>
        <th>@string.Format("{0:0.00}", Math.Truncate(user.Totals.Doubletime * 100) / 100)</th>
        <th>@string.Format("{0:0.00}", Math.Truncate(user.Totals.Sick * 100) / 100)</th>
        <th>@string.Format("{0:0.00}", Math.Truncate(user.Totals.Vacation * 100) / 100)</th>
        <th>@string.Format("{0:0.00}", Math.Truncate(user.Totals.Holiday * 100) / 100)</th>
        <th>@string.Format("{0:0.00}", Math.Truncate(user.Totals.Overall * 100) / 100)</th>
    </tr>
</tfoot>

它产生了这个结果:

enter image description here

(在新标签页中打开图片以查看其全尺寸。)

如果您查看最右侧列中的值,您会注意到他们加起来位于表格右下角的79.98。我计算出它们加起来为79.93

因为我知道有人会问,是的,79.98是正确的总数。它是所谓的的值,以加起来总计不正确。

我做错了什么?我已经花了很长时间摆弄这个并且没有看到任何变化。

修改

在阅读了一些评论之后,很清楚Math.Truncate()来电没有帮助。这就是我以前的所作所为:

<tbody>
    @foreach (var day in user.WorkDays)
    {
        <tr>
            <th>@day.Date.ToString("MM/dd/yy")</th>
            <td>
                <ul>
                    @foreach (var note in day.Notes)
                    {
                        <li>@note.Text</li>
                    }
                </ul>
            </td>
            <td>@day.Totals.Regular.ToString("0.00")</td>
            <td>@day.Totals.Overtime.ToString("0.00")</td>
            <td>@day.Totals.Doubletime.ToString("0.00")</td>
            <td>@day.Totals.Sick.ToString("0.00")</td>
            <td>@day.Totals.Vacation.ToString("0.00")</td>
            <td>@day.Totals.Holiday.ToString("0.00")</td>
            <td>@day.Totals.Overall.ToString("0.00")</td>
        </tr>
    }
</tbody>
<tfoot>
    <tr>
        <th>Totals:</th>
        <th></th>
        <th>@user.Totals.Regular.ToString("0.00")</th>
        <th>@user.Totals.Overtime.ToString("0.00")</th>
        <th>@user.Totals.Doubletime.ToString("0.00")</th>
        <th>@user.Totals.Sick.ToString("0.00")</th>
        <th>@user.Totals.Vacation.ToString("0.00")</th>
        <th>@user.Totals.Holiday.ToString("0.00")</th>
        <th>@user.Totals.Overall.ToString("0.00")</th>
    </tr>
</tfoot>

现在,这些值甚至加起来为80。即使那个.02高于实际价值。

3 个答案:

答案 0 :(得分:4)

所以这是真正的问题 - “总数”应该是原始数字或截断数字的总和。 (作为旁注,ROUND减少错误,但消除错误。)。如果总数应该是原始数字的总和,那么您可以:

  1. 单独留下报告,并在脚注中解释由于四舍五入,总数可能不完全匹配。
  2. 为“舍入错误”添加一行。
  3. 将舍入错误分配给一个或多个项目(here's我有关于如何做的问题和论点)
  4. 如果总数应该是舍入数字的总和,那么在报告中这很容易做到,但它与原始数字的总数不匹配(除非你将原始数字四舍五入在来源)。

答案 1 :(得分:3)

你真的应该读这篇论文:

  大卫戈德伯格。 1991年。   每个计算机科学家应该了解浮点运算。   ACM Comput。监测网。 23,1(1991年3月),5-48。 DOI = 10.1145 / 103162.103163   http://doi.acm.org/10.1145/103162.103163

  

<强>抽象   浮点算术被许多人视为深奥的主题。   这是相当令人惊讶的,因为浮点在计算机系统中无处不在:   几乎每种语言都有浮点数据类型;从PC到PC的电脑   超级计算机有浮点加速器;大多数编译器都会被调用   不时编译浮点算法;几乎每个操作系统   必须响应溢出等浮点异常。本文介绍了一个   关于浮点数方面的教程,对设计者有直接影响   计算机系统。它以浮点表示和舍入的背景开始   错误,继续讨论IEEE浮点标准,并得出结论   以及计算机系统构建器如何更好地支持浮点的示例。

但问题很可能是您要么截断或舍入显示值并计算原始值的总和。因此delta。

如果您希望它们匹配:

  • 将显示的值相加
  • 使用decimal代替double

答案 2 :(得分:2)

这里真正的问题不是string.Format()。这里的问题是Math.Truncate()。

请允许我演示:

Math.Truncate(4.499999 * 100) / 100 = 4.49
Math.Round(4.499999, 2) = 4.50

现在.01是一个相对较小的数量,但你的计算方法是导致问题的原因。你的例子有14行。如果每一行都是错误的,那么如果人们自己计算它(即使你的总数是正确的),你的显示就会出错。