เทคนิคการประมาณค่าใดที่มีอยู่สำหรับการคำนวณค่ารูตที่สอง?


12

ฉันมีทรัพยากรที่ จำกัด มากเพราะฉันทำงานกับไมโครคอนโทรลเลอร์ มีการขยายตัวของเทย์เลอร์ชุดตารางการค้นหาทั่วไปหรือวิธีการเรียกซ้ำ?

ฉันต้องการทำบางอย่างโดยไม่ใช้ sqrt ของ math.h ()

http://www.cplusplus.com/reference/cmath/sqrt/


5
ลองดูลิงค์นี้: codeproject.com/Articles/69941/…
Matt L.

1
ยกเว้นความจริงที่ว่ามันเป็นคำถามเพิ่มเติมเกี่ยวกับการเขียนโปรแกรมทำไมไม่ลองตอบคำถามของ Matt?
jojek

อินพุตจุดลอยหรือจุดคงที่? สำหรับจุดคงที่วิธีการวนซ้ำอาจจะดีกว่า แต่ฉันจะไม่รบกวนอธิบายถ้าคุณไม่ต้องการมัน
ออสการ์

@Oscar ฉันชอบที่จะเรียนรู้วิธีจุดคงที่ในขณะที่ฉันพยายามไม่ต้องใช้ Float ในเฟิร์มแวร์ของฉัน :)
tarabyte

คำตอบ:


13

หากคุณต้องการการขยายตัวซีรีย์พลังงานราคาถูกและสกปรก (ค่าสัมประสิทธิ์สำหรับซีรีส์เทย์เลอร์มาบรรจบกันอย่างช้าๆ) สำหรับsqrt()และกลุ่มผู้อ่านรายอื่น ๆ ฉันมีรหัสมานานแล้ว ฉันเคยขายรหัสนี้ แต่ไม่มีใครจ่ายเงินให้ฉันมาเกือบสิบปีแล้ว ดังนั้นฉันคิดว่าฉันจะปล่อยมันเพื่อการบริโภคสาธารณะ ไฟล์นี้โดยเฉพาะสำหรับแอปพลิเคชันที่โปรเซสเซอร์มีจุดลอยตัว (ความแม่นยำเดี่ยว IEEE-754) และพวกเขามี C คอมไพเลอร์และระบบ dev แต่พวกเขาไม่ได้มี (หรือพวกเขาไม่ต้องการที่จะเชื่อมโยง) stdlib ที่จะมีฟังก์ชั่นคณิตศาสตร์มาตรฐาน พวกเขาไม่ต้องการความแม่นยำสมบูรณ์แบบ แต่พวกเขาต้องการให้สิ่งต่าง ๆ เป็นไปอย่างรวดเร็ว คุณสามารถย้อนรหัสได้อย่างง่ายดายเพื่อดูว่าสัมประสิทธิ์ซีรีย์พลังงานคืออะไรและเขียนรหัสของคุณเอง รหัสนี้จะถือว่า IEEE-754 และปิดบังบิตสำหรับ mantissa และเลขชี้กำลัง

ดูเหมือนว่า "รหัสมาร์กอัป" ที่ SE มีไม่เป็นมิตรกับตัวอักษรมุม (คุณรู้ว่า ">" หรือ "<") ดังนั้นคุณอาจต้องกด "แก้ไข" เพื่อดูทั้งหมด

//
//    FILE: __functions.h
//
//    fast and approximate transcendental functions
//
//    copyright (c) 2004  Robert Bristow-Johnson
//
//    rbj@audioimagination.com
//


#ifndef __FUNCTIONS_H
#define __FUNCTIONS_H

#define TINY 1.0e-8
#define HUGE 1.0e8

#define PI              (3.1415926535897932384626433832795028841972)        /* pi */
#define ONE_OVER_PI     (0.3183098861837906661338147750939)
#define TWOPI           (6.2831853071795864769252867665590057683943)        /* 2*pi */
#define ONE_OVER_TWOPI  (0.15915494309189535682609381638)
#define PI_2            (1.5707963267948966192313216916397514420986)        /* pi/2 */
#define TWO_OVER_PI     (0.636619772367581332267629550188)
#define LN2             (0.6931471805599453094172321214581765680755)        /* ln(2) */
#define ONE_OVER_LN2    (1.44269504088896333066907387547)
#define LN10            (2.3025850929940456840179914546843642076011)        /* ln(10) */
#define ONE_OVER_LN10   (0.43429448190325177635683940025)
#define ROOT2           (1.4142135623730950488016887242096980785697)        /* sqrt(2) */
#define ONE_OVER_ROOT2  (0.707106781186547438494264988549)

#define DB_LOG2_ENERGY          (3.01029995663981154631945610163)           /* dB = DB_LOG2_ENERGY*__log2(energy) */
#define DB_LOG2_AMPL            (6.02059991327962309263891220326)           /* dB = DB_LOG2_AMPL*__log2(amplitude) */
#define ONE_OVER_DB_LOG2_AMPL   (0.16609640474436811218256075335)           /* amplitude = __exp2(ONE_OVER_DB_LOG2_AMPL*dB) */

#define LONG_OFFSET     4096L
#define FLOAT_OFFSET    4096.0



float   __sqrt(float x);

float   __log2(float x);
float   __exp2(float x);

float   __log(float x);
float   __exp(float x);

float   __pow(float x, float y);

float   __sin_pi(float x);
float   __cos_pi(float x);

float   __sin(float x);
float   __cos(float x);
float   __tan(float x);

float   __atan(float x);
float   __asin(float x);
float   __acos(float x);

float   __arg(float Imag, float Real);

float   __poly(float *a, int order, float x);
float   __map(float *f, float scaler, float x);
float   __discreteMap(float *f, float scaler, float x);

unsigned long __random();

#endif




//
//    FILE: __functions.c
//
//    fast and approximate transcendental functions
//
//    copyright (c) 2004  Robert Bristow-Johnson
//
//    rbj@audioimagination.com
//

#define STD_MATH_LIB 0

#include "__functions.h"

#if STD_MATH_LIB
#include "math.h"   // angle brackets don't work with SE markup
#endif




float   __sqrt(register float x)
    {
#if STD_MATH_LIB
    return (float) sqrt((double)x);
#else
    if (x > 5.877471754e-39)
        {
        register float accumulator, xPower;
        register long intPart;
        register union {float f; long i;} xBits;

        xBits.f = x;

        intPart = ((xBits.i)>>23);                  /* get biased exponent */
        intPart -= 127;                             /* unbias it */

        x = (float)(xBits.i & 0x007FFFFF);          /* mask off exponent leaving 0x800000*(mantissa - 1) */
        x *= 1.192092895507812e-07;                 /* divide by 0x800000 */

        accumulator =  1.0 + 0.49959804148061*x;
        xPower = x*x;
        accumulator += -0.12047308243453*xPower;
        xPower *= x;
        accumulator += 0.04585425015501*xPower;
        xPower *= x;
        accumulator += -0.01076564682800*xPower;

        if (intPart & 0x00000001)
            {
            accumulator *= ROOT2;                   /* an odd input exponent means an extra sqrt(2) in the output */
            }

        xBits.i = intPart >> 1;                     /* divide exponent by 2, lose LSB */
        xBits.i += 127;                             /* rebias exponent */
        xBits.i <<= 23;                             /* move biased exponent into exponent bits */

        return accumulator * xBits.f;
        }
     else
        {
        return 0.0;
        }
#endif
    }




float   __log2(register float x)
    {
#if STD_MATH_LIB
    return (float) (ONE_OVER_LN2*log((double)x));
#else
    if (x > 5.877471754e-39)
        {
        register float accumulator, xPower;
        register long intPart;

        register union {float f; long i;} xBits;

        xBits.f = x;

        intPart = ((xBits.i)>>23);                  /* get biased exponent */
        intPart -= 127;                             /* unbias it */

        x = (float)(xBits.i & 0x007FFFFF);          /* mask off exponent leaving 0x800000*(mantissa - 1) */
        x *= 1.192092895507812e-07;                 /* divide by 0x800000 */

        accumulator = 1.44254494359510*x;
        xPower = x*x;
        accumulator += -0.71814525675041*xPower;
        xPower *= x;
        accumulator += 0.45754919692582*xPower;
        xPower *= x;
        accumulator += -0.27790534462866*xPower;
        xPower *= x;
        accumulator += 0.12179791068782*xPower;
        xPower *= x;
        accumulator += -0.02584144982967*xPower;

        return accumulator + (float)intPart;
        }
     else
        {
        return -HUGE;
        }
#endif
    }


float   __exp2(register float x)
    {
#if STD_MATH_LIB
    return (float) exp(LN2*(double)x);
#else
    if (x >= -127.0)
        {
        register float accumulator, xPower;
        register union {float f; long i;} xBits;

        xBits.i = (long)(x + FLOAT_OFFSET) - LONG_OFFSET;       /* integer part */
        x -= (float)(xBits.i);                                  /* fractional part */

        accumulator = 1.0 + 0.69303212081966*x;
        xPower = x*x;
        accumulator += 0.24137976293709*xPower;
        xPower *= x;
        accumulator += 0.05203236900844*xPower;
        xPower *= x;
        accumulator += 0.01355574723481*xPower;

        xBits.i += 127;                                         /* bias integer part */
        xBits.i <<= 23;                                         /* move biased int part into exponent bits */

        return accumulator * xBits.f;
        }
     else
        {
        return 0.0;
        }
#endif
    }


float   __log(register float x)
    {
#if STD_MATH_LIB
    return (float) log((double)x);
#else
    return LN2*__log2(x);
#endif
    }

float   __exp(register float x)
    {
#if STD_MATH_LIB
    return (float) exp((double)x);
#else
    return __exp2(ONE_OVER_LN2*x);
#endif
    }

float   __pow(float x, float y)
    {
#if STD_MATH_LIB
    return (float) pow((double)x, (double)y);
#else
    return __exp2(y*__log2(x));
#endif
    }




float   __sin_pi(register float x)
    {
#if STD_MATH_LIB
    return (float) sin(PI*(double)x);
#else
    register float accumulator, xPower, xSquared;

    register long evenIntPart = ((long)(0.5*x + 1024.5) - 1024)<<1;
    x -= (float)evenIntPart;

    xSquared = x*x;
    accumulator = 3.14159265358979*x;
    xPower = xSquared*x;
    accumulator += -5.16731953364340*xPower;
    xPower *= xSquared;
    accumulator += 2.54620566822659*xPower;
    xPower *= xSquared;
    accumulator += -0.586027023087261*xPower;
    xPower *= xSquared;
    accumulator += 0.06554823491427*xPower;

    return accumulator;
#endif
    }


float   __cos_pi(register float x)
    {
#if STD_MATH_LIB
    return (float) cos(PI*(double)x);
#else
    register float accumulator, xPower, xSquared;

    register long evenIntPart = ((long)(0.5*x + 1024.5) - 1024)<<1;
    x -= (float)evenIntPart;

    xSquared = x*x;
    accumulator = 1.57079632679490*x;                       /* series for sin(PI/2*x) */
    xPower = xSquared*x;
    accumulator += -0.64596406188166*xPower;
    xPower *= xSquared;
    accumulator += 0.07969158490912*xPower;
    xPower *= xSquared;
    accumulator += -0.00467687997706*xPower;
    xPower *= xSquared;
    accumulator += 0.00015303015470*xPower;

    return 1.0 - 2.0*accumulator*accumulator;               /* cos(w) = 1 - 2*(sin(w/2))^2 */
#endif
    }


float   __sin(register float x)
    {
#if STD_MATH_LIB
    return (float) sin((double)x);
#else
    x *= ONE_OVER_PI;
    return __sin_pi(x);
#endif
    }

float   __cos(register float x)
    {
#if STD_MATH_LIB
    return (float) cos((double)x);
#else
    x *= ONE_OVER_PI;
    return __cos_pi(x);
#endif
    }

float   __tan(register float x)
    {
#if STD_MATH_LIB
    return (float) tan((double)x);
#else
    x *= ONE_OVER_PI;
    return __sin_pi(x)/__cos_pi(x);
#endif
    }




float   __atan(register float x)
    {
#if STD_MATH_LIB
    return (float) atan((double)x);
#else
    register float accumulator, xPower, xSquared, offset;

    offset = 0.0;

    if (x < -1.0)
        {
        offset = -PI_2;
        x = -1.0/x;
        }
     else if (x > 1.0)
        {
        offset = PI_2;
        x = -1.0/x;
        }
    xSquared = x*x;
    accumulator = 1.0;
    xPower = xSquared;
    accumulator += 0.33288950512027*xPower;
    xPower *= xSquared;
    accumulator += -0.08467922817644*xPower;
    xPower *= xSquared;
    accumulator += 0.03252232640125*xPower;
    xPower *= xSquared;
    accumulator += -0.00749305860992*xPower;

    return offset + x/accumulator;
#endif
    }


float   __asin(register float x)
    {
#if STD_MATH_LIB
    return (float) asin((double)x);
#else
    return __atan(x/__sqrt(1.0 - x*x));
#endif
    }

float   __acos(register float x)
    {
#if STD_MATH_LIB
    return (float) acos((double)x);
#else
    return __atan(__sqrt(1.0 - x*x)/x);
#endif
    }


float   __arg(float Imag, float Real)
    {
#if STD_MATH_LIB
    return (float) atan2((double)Imag, (double)Real);
#else
    register float accumulator, xPower, xSquared, offset, x;

    if (Imag > 0.0)
        {
        if (Imag <= -Real)
            {
            offset = PI;
            x = Imag/Real;
            }
         else if (Imag > Real)
            {
            offset = PI_2;
            x = -Real/Imag;
            }
         else
            {
            offset = 0.0;
            x = Imag/Real;
            }
        }
     else
        {
        if (Imag >= Real)
            {
            offset = -PI;
            x = Imag/Real;
            }
         else if (Imag < -Real)
            {
            offset = -PI_2;
            x = -Real/Imag;
            }
         else
            {
            offset = 0.0;
            x = Imag/Real;
            }
        }

    xSquared = x*x;
    accumulator = 1.0;
    xPower = xSquared;
    accumulator += 0.33288950512027*xPower;
    xPower *= xSquared;
    accumulator += -0.08467922817644*xPower;
    xPower *= xSquared;
    accumulator += 0.03252232640125*xPower;
    xPower *= xSquared;
    accumulator += -0.00749305860992*xPower;

    return offset + x/accumulator;
#endif
    }




float   __poly(float *a, int order, float x)
    {
    register float accumulator = 0.0, xPower;
    register int n;

    accumulator = a[0];
    xPower = x;
    for (n=1; n<=order; n++)
        {
        accumulator += a[n]*xPower;
        xPower *= x;
        }

    return accumulator;
    }


float   __map(float *f, float scaler, float x)
    {
    register long i;

    x *= scaler;

    i = (long)(x + FLOAT_OFFSET) - LONG_OFFSET;         /* round down without floor() */

    return f[i] + (f[i+1] - f[i])*(x - (float)i);       /* linear interpolate between points */
    }


float   __discreteMap(float *f, float scaler, float x)
    {
    register long i;

    x *= scaler;

    i = (long)(x + (FLOAT_OFFSET+0.5)) - LONG_OFFSET;   /* round to nearest */

    return f[i];
    }


unsigned long __random()
    {
    static unsigned long seed0 = 0x5B7A2775, seed1 = 0x80C7169F;

    seed0 += seed1;
    seed1 += seed0;

    return seed1;
    }

ไม่มีใครรู้ว่ามาร์กอัปโค้ดนี้ทำงานร่วมกับ SE ได้อย่างไร หากคุณกดปุ่ม "แก้ไข" คุณจะเห็นรหัสที่ฉันตั้งใจ แต่สิ่งที่เราดูที่นี่มีโค้ดหลายบรรทัดที่ตัดออกและไม่เพียง แต่ในตอนท้ายของไฟล์ ฉันใช้อ้างอิงมาร์กอัปที่เรากำลังนำไปโดยSE มาร์กอัปช่วยเหลือ หากใครสามารถหาคำตอบได้โปรดแก้ไขคำตอบแล้วบอกสิ่งที่คุณทำ
robert bristow-johnson

ฉันไม่รู้ว่า @Royi คืออะไร
robert bristow-johnson


ดังนั้น @Royi ก็ดีกับฉันว่ารหัสนี้ถูกโพสต์ไปยังตำแหน่ง pastebin หากคุณต้องการนอกจากนี้คุณยังสามารถโพสต์รหัสนี้ที่แปลงไบนารีไปทดสอบทศนิยมและข้อความทศนิยมไบนารี มันถูกใช้ในโครงการฝังตัวเดียวกับที่เราไม่ต้องการstdlibอยู่ในนั้น
robert bristow-johnson

7

หากคุณยังไม่เห็น "Quake square root" นั้นน่าประหลาดใจ มันใช้เวทย์มนตร์ระดับบิตเพื่อให้การประมาณค่าแรกที่ดีมากจากนั้นใช้การประมาณนิวตันสองรอบเพื่อแก้ไข มันอาจช่วยคุณได้หากคุณทำงานกับทรัพยากรที่มี จำกัด

https://en.wikipedia.org/wiki/Fast_inverse_square_root

http://betterexplained.com/articles/understanding-quakes-fast-inverse-square-root/


6

นอกจากนี้คุณยังสามารถใกล้เคียงกับฟังก์ชั่นรากที่สองโดยใช้วิธีของนิวตัน วิธีการของนิวตันเป็นวิธีการประมาณที่รากของฟังก์ชั่นอยู่ นอกจากนี้ยังเป็นวิธีการวนซ้ำที่ใช้ผลลัพธ์จากการวนซ้ำก่อนหน้านี้ในการทำซ้ำครั้งถัดไปจนกว่าการบรรจบกัน สมการสำหรับวิธีการของนิวตันในการเดาว่ารูตนั้นเป็นฟังก์ชันให้เดาเริ่มต้นจะถูกกำหนดเป็น:f(x)x0

x1=x0f(x0)f(x0)

x1เป็นการคาดเดาครั้งแรกว่ามีที่ใดที่เป็นรูต เราทำการรีไซเคิลสมการและใช้ผลลัพธ์จากการทำซ้ำก่อนหน้านี้จนกว่าคำตอบจะไม่เปลี่ยนแปลง โดยทั่วไปในการพิจารณาการเดาของรูทที่การวนซ้ำให้การทายที่การวนซ้ำถูกกำหนดเป็น:(n+1)n

xn+1=xnf(xn)f(xn)

จะใช้วิธีการของนิวตันที่ใกล้เคียงสำหรับรากสมมติว่าเราจะได้รับหมายเลข เช่นการคำนวณรากที่เราต้องคำนวณ เป็นเช่นนี้เราพยายามที่จะหาคำตอบดังกล่าวว่า{a} ตารางทั้งสองฝ่ายและการเคลื่อนย้ายไปทางด้านอื่น ๆ ของอัตราผลตอบแทนสมการ0 ดังนั้นคำตอบของสมการนี้คือและเป็นรากของฟังก์ชัน เช่นนี้ให้เป็นสมการที่เราต้องการหารากของ ด้วยการแทนที่สิ่งนี้ด้วยวิธีของนิวตันและดังนั้น:aax=aax2a=0af(x)=x2af(x)=2x

xn+1=xnxn2a2xn
xn+1=12(xn+axn)

ดังนั้นในการคำนวณสแควร์รูทของเราเพียงแค่ต้องคำนวณวิธีการของนิวตันจนกว่าเราจะมาบรรจบกัน อย่างไรก็ตามตามที่ระบุไว้โดย @ robertbristow-johnson การแบ่งเป็นการดำเนินการที่มีราคาแพงมากโดยเฉพาะอย่างยิ่งสำหรับไมโครคอนโทรลเลอร์ / DSP ที่มีทรัพยากร จำกัด นอกจากนี้อาจมีกรณีที่การเดาอาจเป็น 0 ซึ่งจะส่งผลให้เกิดข้อผิดพลาดหารด้วย 0 เนื่องจากการดำเนินการหาร เป็นเช่นนี้สิ่งที่เราสามารถทำได้คือการใช้วิธีของนิวตันและแก้ปัญหาสำหรับซึ่งกันและกันฟังก์ชั่นแทนคือ{a} สิ่งนี้จะหลีกเลี่ยงการแบ่งใด ๆ ตามที่เราจะเห็นในภายหลัง กู้หน้าทั้งสองฝ่ายและการเคลื่อนย้ายไปทางด้านซ้ายมือจึงให้0 ดังนั้นทางแก้ปัญหานี้ก็จะเป็นa1x=aa1x2a=01a{a}} โดยการคูณด้วยเราจะได้ผลลัพธ์ที่ต้องการ อีกครั้งโดยใช้วิธีของนิวตันเราจึงมี:a

xn+1=xnf(xn)f(xn)
xn+1=xn1(xn)2a2(xn)3
xn+1=12(3xn(xn)3a)

อย่างไรก็ตามมีคำเตือนว่าเราควรพิจารณาเมื่อดูสมการข้างต้น สำหรับรากที่สองการแก้ปัญหาควรเป็นค่าบวกดังนั้นเพื่อให้การวนซ้ำ (และผลลัพธ์) เป็นค่าบวกต้องปฏิบัติตามเงื่อนไขต่อไปนี้:

3xn(xn)3a>0
3xn>(xn)3a
(xn)2a<3

ดังนั้น:

(x0)2a<3

ดังนั้นด้วยจำนวนที่เราต้องการคำนวณสแควร์รูทของการเดาเริ่มต้นต้องเป็นไปตามเงื่อนไขข้างต้น ในที่สุดสิ่งนี้จะถูกวางไว้บนไมโครคอนโทรลเลอร์เราสามารถเริ่มต้นด้วยค่าใด ๆ ของ (พูด 1) จากนั้นวนซ้ำและลดค่าของจนกว่าเงื่อนไขข้างต้นจะเป็นที่พอใจ โปรดทราบว่าฉันหลีกเลี่ยงการทำการหารเพื่อคำนวณค่าโดยตรงx0x0x0ควรเป็นส่วนที่เป็นการดำเนินการที่มีราคาแพง เมื่อเรามีการคาดเดาเริ่มต้นให้ทำซ้ำตามวิธีของนิวตัน โปรดทราบว่าขึ้นอยู่กับการคาดเดาเริ่มต้นอาจใช้เวลารวมกันสั้นลงหรือนานขึ้น ทุกอย่างขึ้นอยู่กับว่าคุณอยู่ใกล้กับคำตอบจริงมากแค่ไหน คุณสามารถกำหนดจำนวนการวนซ้ำหรือรอจนกว่าความแตกต่างสัมพัทธ์ระหว่างสองรากน้อยกว่าขีด จำกัด บางอย่าง (เช่นหรือมากกว่านั้น)106

ในขณะที่แท็กของคุณกำลังมองหาอัลกอริทึมCเราจะเขียนอย่างรวดเร็ว:

#include <stdio.h> // For printf
#include <math.h> // For fabs
void main() 
{
   float a = 5.0; // Number we want to take the square root of
   float x = 1.0; // Initial guess
   float xprev; // Root for previous iteration
   int count; // Counter for iterations

   // Find a better initial guess
   // Half at each step until condition is satisfied
   while (x*x*a >= 3.0)
       x *= 0.5;

   printf("Initial guess: %f\n", x);

   count = 1; 
   do { 
       xprev = x; // Save for previous iteration
       printf("Iteration #%d: %f\n", count++, x);                   
       x = 0.5*(3*xprev - (xprev*xprev*xprev)*a); // Find square root of the reciprocal
   } while (fabs(x - xprev) > 1e-6); 

   x *= a; // Actual answer - Multiply by a
   printf("Square root is: %f\n", x);
   printf("Done!");
}

นี่เป็นการใช้งานขั้นพื้นฐานของวิธีการของนิวตัน โปรดทราบว่าฉันลดการเดาครั้งแรกลงครึ่งหนึ่งจนกว่าเงื่อนไขที่เราพูดถึงก่อนหน้านี้จะพึงพอใจ ฉันพยายามค้นหาสแควร์รูทของ 5 เรารู้ว่านี่เท่ากับ 2.236 หรือประมาณนั้น การใช้โค้ดด้านบนให้ผลลัพธ์ต่อไปนี้:

Initial guess: 0.500000
Iteration #1: 0.500000
Iteration #2: 0.437500
Iteration #3: 0.446899
Iteration #4: 0.447213
Square root is: 2.236068
Done!

โปรดทราบว่าวิธีการของนิวตันคือการหาวิธีการแก้ปัญหาของการแก้ปัญหาซึ่งกันและกันและเราคูณด้วยที่สิ้นสุดจะได้คำตอบสุดท้ายของเรา นอกจากนี้โปรดทราบว่าการเดาเริ่มต้นนั้นเปลี่ยนไปเพื่อให้แน่ใจว่าเกณฑ์ที่ฉันพูดถึงข้างต้นนั้นเป็นที่น่าพอใจ เพื่อความสนุกลองค้นหาสแควร์รูทของ 9876a

Initial guess: 0.015625
Iteration #1: 0.015625
Iteration #2: 0.004601
Iteration #3: 0.006420
Iteration #4: 0.008323
Iteration #5: 0.009638
Iteration #6: 0.010036
Iteration #7: 0.010062
Square root is: 99.378067
Done!

อย่างที่คุณเห็นสิ่งเดียวที่แตกต่างคือต้องมีการคำนวณซ้ำหลายครั้งเพื่อคำนวณสแควร์รูท ยิ่งจำนวนสิ่งที่คุณต้องการคำนวณมากเท่าไหร่ก็จะต้องใช้การทำซ้ำมากขึ้นเท่านั้น

ฉันรู้ว่าวิธีการนี้ได้รับการแนะนำในโพสต์ก่อนหน้านี้ แต่ฉันคิดว่าฉันจะได้รับวิธีการและให้รหัสบางอย่าง!


2
ฉันขอแนะนำให้ฟังก์ชันที่คุณต้องการคือแทน ไม่มีส่วนจะถูกต้องในการทำซ้ำและสิ่งที่คุณต้องทำคือการคูณผลกับจะได้รับ{x} นั่นเป็นเหตุผลที่คุณเห็นทุกสิ่งนี้เกี่ยวกับรากที่สองซึ่งกันและกันในที่มีแสงสว่างและในการใช้งานจริง f(x)=1xxx
robert bristow-johnson

3
สำหรับคนที่เข้ารหัส DSP และชิปอื่น ๆ การแบ่งนั้นมีราคาแพงเป็นพิเศษในขณะที่ชิปเหล่านี้สามารถคูณตัวเลขได้เร็วเท่าที่พวกเขาสามารถย้ายตัวเลขได้
robert bristow-johnson

1
@ robertbristow-johnson - และอีกหนึ่งจุดที่ยอดเยี่ยม ฉันจำได้ว่าเมื่อฉันทำงานกับโมโตโรล่า 6811 การคูณนั้นใช้เวลาไม่กี่รอบ ไม่สวย
rayryeng

3
อ่าดี OL '68HC11 มีบางสิ่งจาก 6809 (เหมือนทวีคูณอย่างรวดเร็ว) แต่มีไมโครคอนโทรลเลอร์มากกว่า
robert bristow-johnson

1
@ robertbristow-johnson - ใช่แล้ว 68HC11 :) ฉันใช้มันเพื่อสร้างระบบสร้างสัญญาณชีวการแพทย์ที่สร้างสัญญาณหัวใจเทียมเพื่อปรับอุปกรณ์ทางการแพทย์และฝึกอบรมนักศึกษาแพทย์ มันเป็นเวลานาน แต่ความทรงจำที่น่ารักมาก!
rayryeng

6

เนื่องจากดูเหมือนว่ารหัสมาร์กอัปของ SE จะทำงานเหมือนอึฉันจะพยายามตอบคำถามนี้โดยตรงมากขึ้นโดยเฉพาะสำหรับฟังก์ชั่นx

ใช่ชุดพลังงานสามารถอย่างรวดเร็วและมีประสิทธิภาพประมาณฟังก์ชั่นสแควร์รูทและเฉพาะในโดเมนที่ จำกัด ยิ่งโดเมนมีข้อกำหนดมากเท่าใดคุณจะต้องใช้ข้อกำหนดเพิ่มเติมในซีรีย์พลังงานเพื่อให้เกิดข้อผิดพลาดต่ำ

สำหรับ1x2

x  1+a1(x1)+a2(x1)2+a3(x1)3+a4(x1)4=1+(x1)(a1+(x1)(a2+(x1)(a3+(x1)a4)))

ที่ไหน

a1 = 0.49959804148061

a2 = -0.12047308243453

a3 = 0.04585425015501

a4 = -0.01076564682800

สัมประสิทธิ์เหล่านี้ถูกกำหนดโดยใช้อัลกอริธึมการแลกเปลี่ยน Remez ที่ปรับเปลี่ยนเช่นความเท่าเทียมกันคือที่และและข้อผิดพลาดสัมพัทธ์สูงสุดในระหว่างนั้นจะลดลงx = 2x=1x=2

ดังนั้นหากการติดตั้งของคุณเป็นจุดคงที่คุณจะต้องเลื่อนบิตของคุณ (นับจำนวนตำแหน่งบิตที่เลื่อน) จนกว่าค่าของคุณจะอยู่ระหว่าง 1 ถึง 2 โดยใช้การปรับสเกลของจุดคงที่ของคุณ หากคุณเลื่อนไปทางขวา [ซ้าย] การโต้แย้งด้วยบิตเพื่อให้อาร์กิวเมนต์อยู่ระหว่าง 1 ถึง 2 ผลลัพธ์ควรเลื่อนไปทางซ้าย [ขวา] โดยบิต หากจำนวนของ shift shift เป็นเลขคี่การเลื่อนบิตพิเศษจะถูกชดเชยด้วยการคูณเพิ่มเติมของซึ่งสามารถเก็บเป็นค่าคงที่ในรหัสของคุณn 2nn2

หากเป็นจุดลอยคุณจะต้องแยกเลขชี้กำลังและ mantissa เหมือนรหัส C ของฉันในคำตอบอื่น


4

จริงๆแล้วมันทำโดยการแก้สมการกำลังสองโดยใช้วิธีของนิวตัน:

http://en.wikipedia.org/wiki/Methods_of_computing_square_roots

สำหรับตัวเลขที่มากกว่าหนึ่งคุณสามารถใช้การขยายเทย์เลอร์ต่อไปนี้:

http://planetmath.org/taylorexpansionofsqrt1x


3

a>b

a2+b20.96a+0.4b.

แม่นยำภายใน 4% ถ้าฉันจำได้ดี มันถูกใช้โดยวิศวกรก่อนผู้ปกครองลอการิทึมและเครื่องคิดเลข ฉันเรียนรู้มันในNotes et formules de l'ingénieur, De Laharpe , 1923

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.