56 messaggi dal 07 marzo 2006
Ciao a tutti,
sto impazzendo con una cosa di cui non riesco a capacitarmi (e che, se qualcuno non mi fornisce una spiegazione plausibile, mi sembra decisamente grave).
Ho incontrato il problema nello sviluppo di un'applicazione più grande, ma sono riuscito a riprodurlo anche con un semplicissimo progetto WPF.
Lo scopo del pezzo di codice è di eliminare i decimali non significativi da un numero (nell'esempio tengo solo il primo decimale).
Per riprodurre il problema ho creato una semplicissima applicazione WPF, il cui codice xaml della finestra principale è

<Window x:Class="TestArrotondamento.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="100" Width="300">
  <DockPanel>
    <TextBox Name="txtValue1" DockPanel.Dock="Top" />
    <TextBox Name="txtValue2" DockPanel.Dock="Top" />
    <Button Name="btnClick" Content="Click" Click="btnClick_Click" />
  </DockPanel>
</Window>


ed il cui codice c# associato è

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TestArrotondamento
{
  /// <summary>
  /// Interaction logic for Window1.xaml
  /// </summary>
  public partial class Window1 : Window
  {
    public Window1()
    {
      InitializeComponent();
    }

    private void btnClick_Click(object sender, RoutedEventArgs e)
    {
      Single value = Single.Parse(this.txtValue1.Text);
      Single roundedValue = this.RoundValue(value);
      this.txtValue2.Text = String.Format("{0:F1}", roundedValue);
    }

    private Single RoundValue(Single value)
    {
      Single roundedValue = value * 10;
      roundedValue = Convert.ToSingle(Math.Truncate(roundedValue));
      roundedValue /= 10;

      return roundedValue;
    }
  }
}


Compilatelo sia in Debug che in Release, lanciate i due eseguibili da file system (NON eseguiteli all'interno di VS, altrimenti il problema non si verifica) e in entrambi scrivete il numero 24.8 nella prima TextBox. Cliccando sul pulsante avrete 24.8 nell'exe di Debug e 24.7 nell'exe di Release!!!
Non riesco a capire cosa succeda, spero che qualcuno mi chiarisca le idee, perchè non ne vedo un motivo chiaro.
Il problema è facilmente aggirabile scrivendo il numero con le cifre desiderate e facendone il Parse, ma il fatto che uno stesso pezzo di codice si comporti in due maniere diverse se compilato in Debug o Release non mi fornisce molta fiducia, considerando anche che la stragrande maggioranza del codice che sviluppo è per l'analisi di segnali numerici!

Grazie mille,
ciao

Software Department
Aprilia Racing S.r.l.
http://www.racingaprilia.com/

==========================================

L'ipocondria è l'unica malattia che non ho.
(Anonimo)
Ciao! Non sono riuscito a riprodurre il problema che segnali: a me sia l'exe in release che quello in debug danno il risultato corretto...

Matteo Casati
GURU4.net
56 messaggi dal 07 marzo 2006
Grazie lo stesso, la cosa incredibile è che l'ho appena riprodotto (è una cosa talmente strana che sto tentando e ritentando, per essere sicuro di non sbagliare qualcosa, ma il codice è talmente semplice, e succede sempre!).
Ciao

Software Department
Aprilia Racing S.r.l.
http://www.racingaprilia.com/

==========================================

L'ipocondria è l'unica malattia che non ho.
(Anonimo)
56 messaggi dal 07 marzo 2006
Per m.casati: ho appena provato su una macchina a 64 bit con XP 64 e il codice funziona (la mia è a 64 ma con XP 32)! Il tup SO è a 32 bit o 64 bit? Giusto per capire la casistica.
Grazie mille
Ciao

Software Department
Aprilia Racing S.r.l.
http://www.racingaprilia.com/

==========================================

L'ipocondria è l'unica malattia che non ho.
(Anonimo)
Hardware a 64 bit con Windows 7 64 bit

Matteo Casati
GURU4.net
Comunque, a parte la curiosità di capire l'origine del problema, che IMHO ci sta tutta, due consigli:
1) Evita di usare Single o Double per questi scopi: sono tipi floating point che possono dare origine a problemi di arrotondamenti. Il tipo Decimal, che è basato su logaritmi in base 10, è molto più "stabile" in questo senso
2) Per arrotondare a n decimali, usa i metodi Math.Round, Math.Floor o Math.Ceiling

Ciao
56 messaggi dal 07 marzo 2006
Riporto dal forum MSDN, così può essere utile per tutti:


Yes, this is reproducible on x86 (even WOW64) and by design.
Read this article "What every computer scientist should know about floating-point arithmetic". In nutshell, floating-point operations have errors in precision by design.
The difference between debug and release builds here is that JIT will do different 80-bit -> 32-bit floating-point conversions which change the epsilon error and can cause this.

Here's simplified code:

using System;

class HelloWorld
{
static void Main()
{
string valueText = "24.8";
Single value = Single.Parse(valueText);
Console.WriteLine("{0} -> {1}", valueText,
String.Format("{0:F1}", RoundValue(value)));
}
static Single RoundValue(Single value)
{
Single roundedValue = value * 10;
roundedValue = Convert.ToSingle(Math.Truncate(roundedValue));
roundedValue /= 10;
return roundedValue;
}
}



-Karel


Rimane, a mio parere, la questione (non di poco conto), che per quanto ci sia una giustificazione, non è corretto che lo stesso codice dia risultati diversi se eseguito in Debug o Release. Questo, sinceramente, non mi dà un gran senso di sicurezza...

Grazie a tutti,
Marco

Software Department
Aprilia Racing S.r.l.
http://www.racingaprilia.com/

==========================================

L'ipocondria è l'unica malattia che non ho.
(Anonimo)
Interessante!! Comunque Single e Double sono tipi soggetti a queste sfighe e infatti IMHO andrebbero utilizzati per altri scopi (es. risolvere un'equazione, determinare i punti in un grafico, ecc.ecc.). Altrimenti, quando un dato approssimato non è accettabile, usa Decimal e vivi felice

Ciao,
m.

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.