2016-10-11 6 views
1

J'essaie d'utiliser la fonction intrinsèque 'CEILING', mais l'erreur d'arrondi rend difficile l'obtention de ce que je veux parfois. L'exemple de code est juste très simple:Utiliser CEILING sans l'effet d'arrondi Erreur

PROGRAM MAIN 

    IMPLICIT NONE 

    INTEGER, PARAMETER  ::  ppm_kind_double = KIND(1.0D0) 
    REAL(ppm_kind_double)  ::  before,after,dx 

    before = -0.112 
    dx = 0.008 
    after = CEILING(before/dx) 

    WRITE(*,*) before, dx, before/dx, after 

    END 

Et je a obtenu des résultats: enter image description here

La valeur que je donne à « avant » et « dx » dans le code est juste pour la démonstration. Pour ceux avant/dx = -13.5 par exemple, je veux utiliser CEILING pour obtenir -13. Mais pour la photo que je montre, je veux vraiment -14. J'ai considéré l'utilisation de certains arguments comme

IF(ABS(NINT(before/dx) - before/dx) < 0.001) 

Mais ce n'est tout simplement pas beau. Y a-t-il une meilleure façon de faire cela?

Mise à jour:

Je fus surpris de constater que le problème ne se produira pas si je mets les variables constantes dans ppm_kind_double. Donc je suppose que cette "erreur d'arrondi" ne se produira que lorsque le nombre de chiffres pour la précision d'arrondi de la machine que j'utilise est supérieur à ce qui est défini dans ppm_kind_double. Je cours réellement mon programme (pas ce code de démo) sur un groupe, que je ne sais pas au sujet de la précision de machine. Alors peut-être que c'est la précision quadruple sur cette machine qui mène au problème?

Après avoir défini des constantes à double précision:

before = -0.112_ppm_kind_double 
dx = 0.008_ppm_kind_double 

enter image description here

+0

Vous m'avez mis un peu de côté en voulant -14. Il m'a fallu un moment pour réaliser que -0.112/0.008 est exactement -14, mais à cause des erreurs d'arrondi, le résultat est légèrement plus, et le "PLAFOND" amplifie alors cette minuscule erreur. – chw21

+0

@ chw21, en fait, j'ai vu votre commentaire précédent et j'attendais que vous le trouviez. :) – Ruizhi

+2

Étant donné que la précision fait partie du problème ici, il convient de noter que vos constantes littérales ne sont que de simple précision - par ex. '-0.112' devrait probablement être écrit' -0.112_ppm_kind_double'. Ce changement ne résoudra cependant pas le problème sous-jacent. – IanH

Répondre

0

Ceci est un peu difficile, parce que vous ne savez jamais où l'erreur d'arrondi vient. Si dx était juste un petit peu plus grand que 0.008 alors la division before/dx pourrait encore être arrondie à la même valeur, mais maintenant -13 serait la bonne réponse. Cela dit, la méthode la plus commune autour de ce que j'ai vu est juste nudge la valeur précédente jamais si peu dans la direction opposée. Quelque chose comme ceci:

program sign_test 
    use iso_fortran_env 
    implicit none 
    real(kind=real64) :: a, b 
    integer(kind=int32) :: c 
    a = -0.112 
    b = 0.008 
    c = my_ceiling(a/b) 
    print*, a, b, c 
contains 
    function my_ceiling(v) 
     implicit none 
     real(kind=real64), intent(in) :: v 
     integer(kind=int32) :: my_ceiling 
     my_ceiling = ceiling(v - 1d-6, kind=int32) 
    end function my_ceiling 
end program sign_test 

Cela n'aura aucun impact sur la grande majorité des valeurs, mais il y a maintenant quelques valeurs qui s'arrondi de plus que prévu.

+0

Merci chw21, donner l'estimation initiale d'un changement raisonnable est également un bon moyen. – Ruizhi

0

note si vos nombres réels sont fictivement « exacte » à une certaine précision que vous pourriez faire quelque chose comme ceci:

after=nint(1000*before)/nint(1000*dx) 

cela fonctionne pour votre exemple .. vous n'avez pas dit ce que vous attendez pour les valeurs positives et ainsi de suite, donc vous pourriez avoir besoin de travailler un peu.