C-指向双精度和双精度数组之间的差异。将一个转换为另一个?

时间:2011-06-18 08:38:44

标签: c arrays pointers floating-point fortran

我一直在研究物理研究项目。该程序是用C语言编写的,但使用了fortran函数(它叫做“zgesv”,它来自LAPACK和BLAS库)。

这个想法是解决一个方程组。 LHS.X =向量“X”的RHS。 int INFO 传递给zgesv。如果无法求解方程(即LHS是单数),则应该返回一个值!= 0;

尝试通过将我的double *传递给solve函数(解决方案1,在以下代码中)将我的程序作为“正常”运行, INFO 返回为0 - 即使 LHS 是单数。不仅如此,如果我打印出解决方案,这是一个数字灾难 - 一些小,一些零,一些巨大。

如果我手动创建 LHS RHS     LHS [] = {值1,值2,...};     RHS [] = {值1,值2,...}; 然后 INFO 按预期返回3,解决方案等于 RHS (这也是我所期望的。)

如果我声明数组     LHS2 [] = {值1,值2,...};     RHS2 [] = {值1,值2,...}; 并按元素将它们复制到 LHS RHS 元素中,然后将 INFO 返回为8(我觉得它与前一个案例不同,这很奇怪。),解决方案等于 RHS

我觉得这必须是声明数组的两种方式之间的一些根本区别。我真的无法使用功能zgesv获取muck以使其采用我想要的类型,因为A)它是科学界的标准,而B)它是用fortran编写的 - 我从未学过它。

有谁可以解释这里发生了什么?是否有一个简单的(最好是计算上便宜的)修复?我应该将我的double *复制到数组[]中吗?

这是我的程序(为测试而修改):

包括

#include <stdlib.h>
#include <math.h>
#define PI 3.1415926535897932384626433832795029L

#define ERROR_VALUE 911.911


int* getA(int N, char* argv[])
{
  int i;
  int* AMatrix;
  AMatrix = malloc(N * N * sizeof(int));

  if (AMatrix == NULL)
  {
    printf("Failed to allocate memory for AMatrix. Exiting.");
    exit (EXIT_FAILURE);
  }

  for (i = 0; i < N * N; i++)
  {
    AMatrix[i] = atoi(argv[i + 1]);
  }

  return AMatrix;

}

double* generateLHS(int N, int* AMatrix, int TAPs[], long double kal)
{

  double S, C;
  S = sinl(kal);
  C = cosl(kal);
  printf("According to C, Sin(Pi/2) = %.25lf and Cos(Pi/2) = %.25lf", S, C);
  //       S = 1;
  //       C = 0;
  double* LHS;
  LHS = malloc(N * N * 2 * sizeof(double));

  if (LHS == NULL)
  {
    printf("Failed to allocate memory for LHS. Exiting.");
    exit (EXIT_FAILURE);
  }

  int i;
  for (i = 0; i < N * N; i++)
  {
    LHS[2 * i] = -1 * AMatrix[i];  
    LHS[(2 * i) + 1] = 0;
  }


  for (i = 0; i <= 2 * N * N - 2; i = i + (2 * N) + 2)
  {
    LHS[i] = LHS[i] + (2 * C); 
  }

  int j;
  for (i = 0; i <= 3; i++)
  {
    j = 2 * N * TAPs[i] + 2 * TAPs[i];
    LHS[j] = LHS[j] - C;
    LHS[j + 1] = LHS[j + 1] - S;
  }

  return LHS;
}

double* generateRHS(int N, int inputTailAttachmentPoint, long double kal)
{

  double* RHS;
  RHS = malloc(2 * N * sizeof(double));

  int i;
  for (i = 0; i < 2 * N; i++)
  {
    RHS[i] = 0.0;
  }
  RHS[2 * inputTailAttachmentPoint + 1] = - 2 * sin(kal);
  return RHS; 
}


double* solveUsingLUD(int N, double* LHS, double* RHS)
{
  int INFO; /*Info is changed by ZGELSD to 0 if the computation was carried out successfully. Else it changes to some none-zero integer. */
  int ione = 1;
  int LDA = N;
  int LDB = N;
  int n = N;
  int* IPV = malloc(N * sizeof(int));

  if (IPV == NULL)
  {
    printf("Failed to allocate memory for IPV. Exiting.");
    exit (EXIT_FAILURE);
  }

  zgesv_(&n, &ione, LHS, &LDA, IPV, RHS, &LDB, &INFO);
  free(IPV);

  if (INFO != 0)
  {

    printf("\n ERROR: info = %d\n", INFO); 
  }
  return RHS;
}


void printComplexVectors(int numberOfRows, double* matrix)
{
  int i;

  for (i = 0; i < 2 * numberOfRows - 1; i = i + 2)
  {
    printf("%f + %f*i    \n", matrix[i], matrix[i + 1]);
  }
  printf("\n");
}


int main(int argc, char* argv[])
{
  int N = 8;
  int* AMatrix;
  AMatrix = getA(N, argv);
  int TAPs[]={4,4,4,3};
  long double kal = PI/2;




  double *LHS, *RHS;
  LHS = generateLHS(N, AMatrix, TAPs,kal);
  int i;
  RHS = generateRHS(N, TAPs[0],kal);





  printf("\n LHS = \n{{");
  for (i = 0; i < 2 * N * N - 1;)
  {
    printf("%lf + ", LHS[i]);
    i = i + 1;
    printf("%lfI", LHS[i]);
    i = i + 1;

    if ((int)(.5 * i) % N == 0)
    {
      printf("},\n{"); 
    }
    else
    {
      printf(",");
    }
  }
  printf("}");


  double LHS2[] = {0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,-0.000000,-3.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,-1.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.00000};
  double RHS2[] ={0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,-2.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000};

  printf("comparing LHS and LHS2\n");
  for (i = 0; i < 2 * N * N;)
  {
    if (LHS[i] != LHS2[i]) {
      printf( "LHS difference at index %d\n", i);
      printf("LHS[%d] = %.16lf\n", i, LHS[i]);
  printf("LHS2[%d] = %.16lf\n", i, LHS2[i]);
  printf("The difference is %.16lf\n", LHS[i] - LHS2[i]);
    }
    i = i + 1;
  }

  printf("\n");
  printf("comparing RHS and RHS2\n");
  for (i = 0; i < 2 * N;)
  {
    if (RHS[i] != RHS2[i]) {
      printf( "RHS difference at index %d\n", i);
      printf("RHS[%d] = %.16lf\n", i, RHS[i]);
  printf("RHS2[%d] = %.16lf\n", i, RHS2[i]);
  printf("The difference is %.16lf", RHS[i] - RHS2[i]);

    }
    i = i + 1;
  }

  printf("\n");


  double *solution;

  solution = solveUsingLUD(N,LHS,RHS);
  printf("\n Solution = \n{");
  for (i = 0; i < 2 * N - 1;)
  {
    printf("{%.16lf + ", solution[i]);
    i = i + 1;
    printf("%.16lfI},", solution[i]);
    i = i + 1;
    printf("\n");
    }
    solution = solveUsingLUD(N,LHS2,RHS2);


    printf("Solution2 = \n{");
    for (i = 0; i < 2 * N - 1;)
    {
      printf("{%lf + ", solution[i]);
      i = i + 1;
      printf("%lfI},", solution[i]);
      i = i + 1;
      printf("\n");
    }


    for (i = 0; i < 2 * N * N;)
    {
      LHS[i] = LHS2[i];
      i = i + 1;
    }


    for (i = 0; i < 2 * N;)
    {
      RHS[i] = RHS2[i];
      i = i + 1;
    }
    solution = solveUsingLUD(N,LHS,RHS);

    printf("Solution3 = \n{");
    for (i = 0; i < 2 * N - 1;)
    {
      printf("{%lf + ", solution[i]);
      i = i + 1;
      printf("%lfI},", solution[i]);
      i = i + 1;
      printf("\n");
    }
    return 0;
  }

我使用编译行

gcc -lm -llapack -lblas PiecesOfCprogarm.c -Wall -g

我执行:

./a.out 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0

给出输出

According to C, Sin(Pi/2) = 1.0000000000000000000000000 and Cos(Pi/2) = -0.0000000000000000000271051
 LHS = 
{{-0.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I},
{0.000000 + 0.000000I,-0.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I},
{0.000000 + 0.000000I,0.000000 + 0.000000I,-0.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I},
{0.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I,-0.000000 + -1.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I},
{-1.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + -3.000000I,0.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I},
{-1.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I,-0.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I},
{0.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I,0.000000 + 0.000000I,-0.000000 + 0.000000I,0.000000 + 0.000000I},
{0.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I,-1.000000 + 0.000000I,0.000000 + 0.000000I,0.000000 + 0.000000I,-0.000000 + 0.000000I},
{}comparing LHS and LHS2
LHS difference at index 0
LHS[0] = -0.0000000000000000
LHS2[0] = 0.0000000000000000
The difference is -0.0000000000000000
LHS difference at index 18
LHS[18] = -0.0000000000000000
LHS2[18] = 0.0000000000000000
The difference is -0.0000000000000000
LHS difference at index 36
LHS[36] = -0.0000000000000000
LHS2[36] = 0.0000000000000000
The difference is -0.0000000000000000
LHS difference at index 54
LHS[54] = -0.0000000000000000
LHS2[54] = 0.0000000000000000
The difference is -0.0000000000000000
LHS difference at index 72
LHS[72] = 0.0000000000000000
LHS2[72] = -0.0000000000000000
The difference is 0.0000000000000000
LHS difference at index 90
LHS[90] = -0.0000000000000000
LHS2[90] = 0.0000000000000000
The difference is -0.0000000000000000
LHS difference at index 108
LHS[108] = -0.0000000000000000
LHS2[108] = 0.0000000000000000
The difference is -0.0000000000000000
LHS difference at index 126
LHS[126] = -0.0000000000000000
LHS2[126] = 0.0000000000000000
The difference is -0.0000000000000000

comparing RHS and RHS2


 Solution = 
{{1.0000000000000000 + -0.0000000000000000I},
{-1.0000000000000000 + -0.0000000000000000I},
{-0.0000000000000000 + 0.0000000000000000I},
{-0.0000000000000000 + 0.0000000000000000I},
{0.6000000000000000 + 0.2000000000000000I},
{-0.0000000000000000 + -0.0000000000000000I},
{-6854258945071195.0000000000000000 + 4042255275298396.0000000000000000I},
{6854258945071195.0000000000000000 + -4042255275298396.0000000000000000I},

 ERROR: info = 3
Solution2 = 
{{0.000000 + 0.000000I},
{0.000000 + 0.000000I},
{0.000000 + 0.000000I},
{0.000000 + 0.000000I},
{0.000000 + -2.000000I},
{0.000000 + 0.000000I},
{0.000000 + 0.000000I},
{0.000000 + 0.000000I},

 ERROR: info = 8
Solution3 = 
{{0.000000 + 0.000000I},
{0.000000 + 0.000000I},
{0.000000 + 0.000000I},
{0.000000 + 0.000000I},
{0.000000 + -2.000000I},
{0.000000 + 0.000000I},
{0.000000 + 0.000000I},
{0.000000 + 0.000000I},

4 个答案:

答案 0 :(得分:3)

你的代码中有一些语法错误(不匹配的括号和引号),但我想在你复制代码的时候会出现这些错误。

我的猜测是以下情况可能导致问题:

solveSystemByLUD(int N, double *LHS, double* RHS, ...)
{
...
}

这声明了一个返回int但返回double *的函数。添加返回类型,看看是否有任何变化。

修改:
澄清之后仍然存在一些缺陷 - 由于这一行,代码仍然无法编译:

   * SUBROUTINE ZGESV( N, Nrhs, A, LDA, IPIV, B, LDB, INFO ) Solves A * X = B and stores the answer in B.   If the solver worked, INFO is set to 0.
   */

缺少一个开场评论。

此外,您将sin()和cos()的结果分配给整数变量(默认情况下C和S是整数):

  S = sin(kal);
  C = cos(kal);

从您的代码判断,这不是您想要的。

最后要注意的是,LHS2中缺少最后一个元素(sizeof(LHS2)/sizeof(double)是127而不是2 * 8 * 8 = 128)。这意味着您正在读取和写入数组的末尾,这会导致未定义的行为,并可能导致您看到的问题。

编辑2:
还有一件事:您正在函数getA()中读取argv[0],它始终是可执行文件的路径。你应该从argv[1]开始阅读。为了安全起见,您应该检查用户是否提供了足够的参数(argc - 1 >= N * N)。

答案 1 :(得分:1)

首先:我来自Fortran方面而且我没有C经验,因此我说的可能相关或不相关。

BLAS / LAPACK例程期望接收Fortran数组---本质上是连续内存块的第一个元素的地址。假设数组布局是列主要的,并且大小由LDA控制。基本上没有进行任何检查,所以如果您发送的内容不符合这些期望,那么一切都会破裂。

我从经验中发现的(或者更确切地说 - 看到有人这样做并复制它)从C ++调用LAPACK例程是以下工作:如果使用std::vector<double>来存储矩阵,使用&A[0](其中A是std::vector<double>)调用LAPACK调用,它可以工作。我的(可能太天真)理解是标准库向量是“有点像”C数组,所以我想这可以直接翻译成C.

此外,您正在使用复杂的例程,这些例程可能会或可能不会以微妙的方式更改某些内容。我猜想Fortran的COMPLEX * 16相当于struct{double real_part; double imag_part;}

最后,可以从netlib获得LAPACK例程的参考实现,这里http://www.netlib.org/lapack/complex16/zgesv.f

答案 2 :(得分:0)

这是另一个猜测:

我想知道在generateLHS()和/或generateRHS()内是否生成double值,其中非常小的错误显示为零(或小数部分中至少为零)您使用printf()显示它们,但这些差异会影响zgesv_()中的计算。

您可以尝试在LHS2RHS2声明之后将这些循环添加到您的测试运行中:

printf("comparing LHS and LHS2\n");
for (i = 0; i < 2 * N * N;)
{
    if (LHS[i] != LHS2[i]) {
        printf( "LHS difference at index %d\n", i);
    }
    i = i + 1;
}
printf("\n");
printf("comparing RHS and RHS2\n");
for (i = 0; i < 2 * N;)
{
    if (RHS[i] != RHS2[i]) {
        printf( "RHS difference at index %d\n", i);
    }
    i = i + 1;
}
printf("\n");

答案 3 :(得分:0)

PI不能完全代表double。因此PI/2也不是。{/ p>

因此sin(kal)cos(kal)不一定分别等于1或0.

(如果您尝试通过将它们分配给“int”来解决此问题,请记住,在进行此类转换时,C通常会将向下舍入。)

[编辑]

其实我有更好的建议......

使用GCC编译Fortran代码时,您可能希望使用-ffloat-store。 Fortran代码通常是在仔细注意双精度数的量化的情况下编写的......但是默认情况下,x86上的中间值可以具有额外的精度(因为浮点单元在内部使用80位)。通常,额外的精度只会有所帮助,但对于某些数字敏感的代码(例如sin(PI / 2)),它可能会导致意外的结果。

-ffloat-store避免了这种额外的精确度。

[编辑2,回应评论]

为了从sin和cos中获得更好的精确度,我建议如下。

首先,将kal声明为long double,同时在main和参数列表中声明为其他函数。

其次,调用sinl()cosl()而不是sin()cos()

第三,像这样定义PI:

#define PI 3.1415926535897932384626433832795029L

将其他所有内容(尤其是SC)保留为double。看看它是否有帮助......