Visualizzazione dei risultati da 1 a 6 su 6
  1. #1
    Utente di HTML.it
    Registrato dal
    May 2009
    Messaggi
    3

    VB6: errore virgola mobile

    Stavo scrivendo un programma dove utilizzavo in modo intensivo la matematica in doppia precisione, quando mi sono accorto di uno strano comportamento di VB6 al quale non riesco dare una spiegazione logica.

    Provate a scrive il codice qui sotto:

    Dim A As Double
    Dim B As Double
    Dim C As Double
    Dim D As Double
    A = 5624.8
    B = 0.1
    C = (A /B)
    D = Int(A /B)

    E' evidente che C e D dovrebbero riportare lo stesso valore intero in quanto A è divisibile per B.
    Con i valori sopra riportati di A e B, C e D dovrebbero essere impostati ambedue a 56248.
    Ebbene, sembra incredibile, ma il risultato delle operazioni in questione, secondo il mio VB6 è:

    C = 56248

    e

    D = 56247 (?!?!)

    Il problema scompare se A e B sono interi oppure se il rapporto A/B non è un intero (in tal caso la funzione Int fa il suo dovere).
    Qualcuno ne sa qualcosa?

    Massimo

  2. #2
    Utente di HTML.it L'avatar di oregon
    Registrato dal
    Jul 2005
    residenza
    Roma
    Messaggi
    36,481
    E' normale ... dipende dagli "errori di rappresentazione dei valori in virgola mobile", inevitabili a causa del metodo con cui i valori decimali sono rappresentati in binario.

    Nel caso indicato, puoi rimediare scrivendo

    D = Int(CDbl(A / B))
    No MP tecnici (non rispondo nemmeno!), usa il forum.

  3. #3
    Utente di HTML.it
    Registrato dal
    May 2009
    Messaggi
    3
    Grazie, ma avevo già risolto il problema dal punto di vista funzionale.
    La ragione per cui ho postato il messaggio, è che mi lascia stupito il fatto che il valore in virgola mobile non troncato dalla funzione Int sia corretto.
    Visto che il risultato di A/B è corretto, significa che le mantisse dei due operatori sono state correttamente allineate, e quindi non sono intervenuti errori di rappresentazione dei numeri razionali decimali usati. Questo mi fa pensare che sia la funzione Int a commettere un errore, perché non si capisce cosa cambi fra Int(A/B) e Int(Cdbl(A/B)) se A e B sono già dei Double. Visto che gli operatori della funzione A/B sono dei Double anche l'accumulatore del risultato dovrebbe essere un Double.
    Evidentemente l'accumulatore di virgola mobile che riceve il rapporto A/B deve contenere qualche anomalia di rappresentazione della mantissa non gestita dalla funzione Int, che invece la funzione Cdbl rimette a posto convertendo da Double a Double l'accumulatore stesso. Anomalia che invece non si presenta se tutti gli operatori della funzione A/B sono dei interi contenuti in una variablie Double.
    Ammetto di non essere un programmatore VB di professione. Normalmente sviluppo su microcontrollori della famiglia 8051 prevalentemente in assembler e talvolta in C, ma cose di questo genere, che io ricordi, in C con le librerie IEEE-754 non mi erano mai capitate!
    Grazie ancora.
    Ciao,
    Massimo

  4. #4
    Utente di HTML.it L'avatar di oregon
    Registrato dal
    Jul 2005
    residenza
    Roma
    Messaggi
    36,481
    Allora ... quando vengono effettuate quelle operazioni succede che il valore contenuto nella variabile A viene caricato nello stack della FPU con

    fld qword ptr ds:[ADDR_A]

    e diviso per quello contenuto in memoria nella variabile B

    fdiv qword ptr ds:[ADDR_B]

    ottenendo (in ST0 della FPU) il risultato

    ST0 = +5.6247999999999998e+0004

    a causa delle imprecisioni della rappresentazione in FP dei valori di cui ti parlavo (specialmente del 0.1).

    In seguito viene richiamata la Int con

    call dword ptr ds:[VBRUNTIME_INT]

    che restituisce (ovviamente) il valore

    ST0 = +5.6247000000000000e+0004

    dato che la funzione tronca il valore.

    Invece, se viene usata la CDbl con il risultato della divisione, prima della Int, il valore di partenza

    ST0 = +5.6247999999999998e+0004

    viene conservato in memoria (nell'area dello stack della CPU) con

    fstp qword ptr [esp]

    e subito ricaricato dalla memoria nella FPU con l'istruzione

    fld qword ptr [esp]

    Questa coppia di istruzioni (fstp/fld) apparentemente inutili servono invece a correggere il valore, perche' la

    FLD

    se l'operando e' la memoria (come indicato nel manuale INTEL 2A, Instruction Set Reference A-M), viene prima convertito nel formato "Double Extended" (80 bit) e quindi corretto in

    ST0 = +5.6248000000000000e+0004

    A questo punto la Int restituirà il valore che ti aspetti.


    Spero che questa spiegazione sia stata chiara e ti sia stata utile.
    No MP tecnici (non rispondo nemmeno!), usa il forum.

  5. #5
    Utente di HTML.it
    Registrato dal
    May 2009
    Messaggi
    3
    Ok,
    ora tutto è chiaro.
    Non conosco il tuo nome, in ogni caso grazie per la dettagliata ed esaustiva spiegazione.

    Ciao,
    Massimo

  6. #6
    Utente di HTML.it L'avatar di oregon
    Registrato dal
    Jul 2005
    residenza
    Roma
    Messaggi
    36,481
    Originariamente inviato da pmax65
    Ok,
    ora tutto è chiaro.
    Non conosco il tuo nome, in ogni caso grazie per la dettagliata ed esaustiva spiegazione.
    Di nulla,

    ciao
    (Antonio)
    No MP tecnici (non rispondo nemmeno!), usa il forum.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.